Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
engine.php
1<?php
3
19use Bitrix\Main\Page\Asset;
24
25Loc::loadMessages(__FILE__);
26
27final class Engine
28{
31
32 private static $instance;
33 private static $isEnabled = false;
34 private static $useHTMLCache = false;
35 private static $onBeforeHandleKey = false;
36 private static $onRestartBufferHandleKey = false;
37 private static $onBeforeLocalRedirect = false;
38 private static $autoUpdate = true;
39 private static $autoUpdateTTL = 0;
40 private static $isCompositeInjected = false;
41 private static $isRedirect = false;
42 private static $isBufferRestarted = false;
43
47 private function __construct()
48 {
49
50 }
51
55 private function __clone()
56 {
57
58 }
59
65 public static function getInstance()
66 {
67 if (is_null(self::$instance))
68 {
69 self::$instance = new Engine();
70 }
71
72 return self::$instance;
73 }
74
82 public static function setEnable($isEnabled = true)
83 {
84 if ($isEnabled && !self::$isEnabled)
85 {
86 self::$onBeforeHandleKey = AddEventHandler(
87 "main",
88 "OnBeforeEndBufferContent",
89 array(__CLASS__, "onBeforeEndBufferContent")
90 );
91 self::$onRestartBufferHandleKey = AddEventHandler(
92 "main",
93 "OnBeforeRestartBuffer",
94 array(__CLASS__, "onBeforeRestartBuffer")
95 );
96 self::$onBeforeLocalRedirect = AddEventHandler(
97 "main",
98 "OnBeforeLocalRedirect",
99 array(__CLASS__, "onBeforeLocalRedirect"),
100 2
101 );
102 self::$isEnabled = true;
103 \CJSCore::init(array("fc"));
104 }
105 elseif (!$isEnabled && self::$isEnabled)
106 {
107 if (self::$onBeforeHandleKey >= 0)
108 {
109 RemoveEventHandler("main", "OnBeforeEndBufferContent", self::$onBeforeHandleKey);
110 }
111
112 if (self::$onRestartBufferHandleKey >= 0)
113 {
114 RemoveEventHandler("main", "OnBeforeRestartBuffer", self::$onRestartBufferHandleKey);
115 }
116
117 if (self::$onBeforeLocalRedirect >= 0)
118 {
119 RemoveEventHandler("main", "OnBeforeLocalRedirect", self::$onBeforeLocalRedirect);
120 }
121
122 self::$isEnabled = false;
123 }
124 }
125
131 public static function isEnabled()
132 {
133 return self::$isEnabled;
134 }
135
143 public static function setUseAppCache($useAppCache = true)
144 {
145 if (self::getUseAppCache())
146 {
147 self::setUseHTMLCache(false);
148 }
149 $appCache = AppCache::getInstance();
150 $appCache->setEnabled($useAppCache);
151 }
152
158 public static function getUseAppCache()
159 {
160 $appCache = AppCache::getInstance();
161
162 return $appCache->isEnabled();
163 }
164
172 public static function setUseHTMLCache($useHTMLCache = true)
173 {
174 self::$useHTMLCache = $useHTMLCache;
175 self::setEnable();
176 }
177
183 public static function getUseHTMLCache()
184 {
185 return self::$useHTMLCache;
186 }
187
193 public static function isAjaxRequest()
194 {
195 return Helper::isAjaxRequest();
196 }
197
198 public static function isInvalidationRequest()
199 {
200 return self::isAjaxRequest() && Context::getCurrent()->getServer()->get("HTTP_BX_INVALIDATE_CACHE") === "Y";
201 }
202
207 public static function isBannerEnabled()
208 {
209 return Option::get("main", "~show_composite_banner", "Y") == "Y";
210 }
211
219 public static function setAutoUpdate($flag)
220 {
221 self::$autoUpdate = $flag === false ? false : true;
222 }
223
228 public static function getAutoUpdate()
229 {
230 return self::$autoUpdate;
231 }
232
240 public static function setAutoUpdateTTL($ttl)
241 {
242 self::$autoUpdateTTL = intval($ttl);
243 }
244
249 public static function getAutoUpdateTTL()
250 {
251 return self::$autoUpdateTTL;
252 }
253
260 public static function onBeforeEndBufferContent()
261 {
262 $params = array();
263 if (self::getUseAppCache())
264 {
265 $manifest = AppCache::getInstance();
266 $params = $manifest->OnBeforeEndBufferContent();
267 $params["CACHE_MODE"] = "APPCACHE";
268 $params["PAGE_URL"] = Context::getCurrent()->getServer()->getRequestUri();
269 }
270 elseif (self::getUseHTMLCache())
271 {
272 $page = Page::getInstance();
273 $page->onBeforeEndBufferContent();
274
275 if ($page->isCacheable())
276 {
277 $params["CACHE_MODE"] = "HTMLCACHE";
278
279 if (self::isBannerEnabled())
280 {
281 $options = Helper::getOptions();
282 $params["banner"] = array(
283 "url" => GetMessage("COMPOSITE_BANNER_URL"),
284 "text" => GetMessage("COMPOSITE_BANNER_TEXT"),
285 "bgcolor" => $options["BANNER_BGCOLOR"] ?? "",
286 "style" => $options["BANNER_STYLE"] ?? ""
287 );
288 }
289 }
290 else
291 {
292 return;
293 }
294 }
295
296 $params["storageBlocks"] = array();
297 $params["dynamicBlocks"] = array();
298 $dynamicAreas = StaticArea::getDynamicAreas();
299 foreach ($dynamicAreas as $id => $dynamicArea)
300 {
301 $stub = $dynamicArea->getStub();
302 self::replaceSessid($stub);
303
304 $params["dynamicBlocks"][$dynamicArea->getId()] = mb_substr(md5($stub), 0, 12);
305 if ($dynamicArea->getBrowserStorage())
306 {
307 $realId = $dynamicArea->getContainerId() !== null ? $dynamicArea->getContainerId() : "bxdynamic_".$id;
308 $params["storageBlocks"][] = $realId;
309 }
310 }
311
312 $params["AUTO_UPDATE"] = self::getAutoUpdate();
313 $params["AUTO_UPDATE_TTL"] = self::getAutoUpdateTTL();
314 $params["version"] = 2;
315
316 Asset::getInstance()->addString(
317 self::getInjectedJs($params),
318 false,
319 AssetLocation::BEFORE_CSS,
320 self::getUseHTMLCache() ? AssetMode::COMPOSITE : AssetMode::ALL
321 );
322
323 self::$isCompositeInjected = true;
324 }
325
332 public static function startBuffering($content)
333 {
334 if (!self::isEnabled() || !is_object($GLOBALS["APPLICATION"]) || !self::$isCompositeInjected)
335 {
336 return null;
337 }
338
339 if (defined("BX_BUFFER_SHUTDOWN") || !defined("B_EPILOG_INCLUDED"))
340 {
341 Logger::log(
342 array(
343 "TYPE" => Logger::TYPE_PHP_SHUTDOWN,
344 )
345 );
346
347 return null;
348 }
349
350 $newBuffer = $GLOBALS["APPLICATION"]->buffer_content;
351 $cnt = count($GLOBALS["APPLICATION"]->buffer_content_type);
352
353 Asset::getInstance()->setMode(AssetMode::COMPOSITE);
354
355 self::$isCompositeInjected = false; //double-check
356 for ($i = 0; $i < $cnt; $i++)
357 {
358 $method = $GLOBALS["APPLICATION"]->buffer_content_type[$i]["F"];
359 if (!is_array($method) || count($method) !== 2 || $method[0] !== $GLOBALS["APPLICATION"])
360 {
361 continue;
362 }
363
364 if (in_array($method[1], array("GetCSS", "GetHeadScripts", "GetHeadStrings")))
365 {
366 $newBuffer[$i * 2 + 1] = call_user_func_array(
367 $method,
368 $GLOBALS["APPLICATION"]->buffer_content_type[$i]["P"]
369 );
370 if (self::$isCompositeInjected !== true && $method[1] === "GetHeadStrings")
371 {
372 self::$isCompositeInjected =
373 strpos($newBuffer[$i * 2 + 1], "w.frameRequestStart") !== false;
374 }
375 }
376 }
377
378 Asset::getInstance()->setMode(AssetMode::STANDARD);
379
380 if (!self::$isCompositeInjected)
381 {
382 Logger::log(
383 array(
384 "TYPE" => Logger::TYPE_COMPOSITE_NOT_INJECTED,
385 )
386 );
387 }
388
389 return self::$isCompositeInjected ? implode("", $newBuffer).$content : null;
390 }
391
401 public static function endBuffering(&$originalContent, $compositeContent)
402 {
403 if (!self::isEnabled() || $compositeContent === null || defined("BX_BUFFER_SHUTDOWN") || !defined("B_EPILOG_INCLUDED"))
404 {
405 //this happens when die() invokes in self::onBeforeLocalRedirect
406 if (self::isAjaxRequest() && self::$isRedirect === false)
407 {
408 $originalContent = self::getAjaxError();
409 Page::getInstance()->delete();
410
411 return true;
412 }
413
414 return false;
415 }
416
417 if (function_exists("getmoduleevents"))
418 {
419 foreach (GetModuleEvents("main", "OnEndBufferContent", true) as $arEvent)
420 {
421 ExecuteModuleEventEx($arEvent, array(&$compositeContent));
422 }
423 }
424
425 $compositeContent = self::processPageContent($compositeContent);
426 if (self::isAjaxRequest() || self::getUseAppCache())
427 {
428 $originalContent = $compositeContent;
429
430 return true;
431 }
432
433 return false;
434 }
435
450 private static function processPageContent($content)
451 {
452 global $APPLICATION, $USER;
453
454 $dividedData = self::getDividedPageData($content);
455 $htmlCacheChanged = false;
456
457 if (self::getUseHTMLCache())
458 {
459 $page = Page::getInstance();
460 if ($page->isCacheable())
461 {
462 $cacheExists = $page->exists();
463 $rewriteCache = $page->getMd5() !== $dividedData["md5"];
464 if (self::getAutoUpdate() && self::getAutoUpdateTTL() > 0 && $cacheExists)
465 {
466 $mtime = $page->getLastModified();
467 if ($mtime !== false && ($mtime + self::getAutoUpdateTTL()) > time())
468 {
469 $rewriteCache = false;
470 }
471 }
472
473 $invalidateCache = self::getAutoUpdate() === false && self::isInvalidationRequest();
474
475 $oldContent = null;
476 if (!$cacheExists || $rewriteCache || $invalidateCache)
477 {
478 if ($invalidateCache || Locker::lock($page->getCacheKey()))
479 {
480 if ($cacheExists && Logger::isOn())
481 {
482 $oldContent = $page->read();
483 }
484
485 if ($page->getStorage() instanceof Data\FileStorage)
486 {
487 $freeSpace = strlen($dividedData["static"]) + strlen($dividedData["md5"]);
488 self::ensureFileQuota($freeSpace);
489 }
490
491 $success = $page->write($dividedData["static"], $dividedData["md5"]);
492
493 if ($success)
494 {
495 $htmlCacheChanged = true;
496 $page->setUserPrivateKey();
497 }
498
499 Locker::unlock($page->getCacheKey());
500 }
501 }
502
503 $pageId = PageManager::register(
504 $page->getCacheKey(),
505 array(
506 "CHANGED" => $htmlCacheChanged,
507 "SIZE" => $page->getSize()
508 )
509 );
510
511 if ($oldContent !== null)
512 {
513 Logger::log(
514 array(
515 "TYPE" => Logger::TYPE_CACHE_REWRITING,
516 "MESSAGE" => $oldContent,
517 "PAGE_ID" => $pageId
518 )
519 );
520 }
521 }
522 else
523 {
524 $page->delete();
525
526 return self::getAjaxError();
527 }
528 }
529
530 if (self::getUseAppCache() == true) //Do we use html5 application cache?
531 {
532 AppCache::getInstance()->generate($dividedData["static"]);
533 }
534 else
535 {
536 AppCache::checkObsoleteManifest();
537 }
538
539 if (self::isAjaxRequest())
540 {
541 self::sendRandHeader();
542
543 header("Content-Type: application/x-javascript; charset=".SITE_CHARSET);
544 header("X-Bitrix-Composite: Ajax ".($htmlCacheChanged ? "(changed)" : "(stable)"));
545
546 $content = array(
547 "js" => array_unique($APPLICATION->arHeadScripts),
548 "lang" => \CJSCore::GetCoreMessages(),
549 "css" => $APPLICATION->GetCSSArray(),
550 "htmlCacheChanged" => $htmlCacheChanged,
551 "isManifestUpdated" => AppCache::getInstance()->getIsModified(),
552 "dynamicBlocks" => $dividedData["dynamic"],
553 "spread" => array_map(array("CUtil", "JSEscape"), $APPLICATION->GetSpreadCookieUrls()),
554 );
555
556 $content = \CUtil::PhpToJSObject($content);
557 }
558 else
559 {
560 $content = $dividedData["static"];
561 }
562
563 return $content;
564 }
565
583 private static function getDividedPageData($content)
584 {
585 $data = array(
586 "dynamic" => array(),
587 "static" => "",
588 "md5" => "",
589 );
590
591 $dynamicAreas = StaticArea::getDynamicAreas();
592 if (!empty($dynamicAreas) && ($areas = self::getFrameIndexes($content)) !== false)
593 {
594 $offset = 0;
595 $pageBlocks = self::getPageBlocks();
596 foreach ($areas as $area)
597 {
598 $dynamicArea = StaticArea::getDynamicArea($area->id);
599 if ($dynamicArea === null)
600 {
601 continue;
602 }
603
604 $realId = $dynamicArea->getContainerId() !== null ? $dynamicArea->getContainerId() : "bxdynamic_".$area->id;
605 $assets = Asset::getInstance()->getAssetInfo($dynamicArea->getAssetId(), $dynamicArea->getAssetMode());
606 $areaContent = substr($content, $area->openTagEnd, $area->closingTagStart - $area->openTagEnd);
607 $areaContentMd5 = substr(md5($areaContent), 0, 12);
608
609 $blockId = $dynamicArea->getId();
610 $hasSameContent = isset($pageBlocks[$blockId]) && $pageBlocks[$blockId] === $areaContentMd5;
611
612 if (!$hasSameContent)
613 {
614 $data["dynamic"][] = array(
615 "ID" => $realId,
616 "CONTENT" => $areaContent,
617 "HASH" => $areaContentMd5,
618 "PROPS" => array(
619 "ID" => $area->id,
620 "CONTAINER_ID" => $dynamicArea->getContainerId(),
621 "USE_BROWSER_STORAGE" => $dynamicArea->getBrowserStorage(),
622 "AUTO_UPDATE" => $dynamicArea->getAutoUpdate(),
623 "USE_ANIMATION" => $dynamicArea->getAnimation(),
624 "CSS" => $assets["CSS"],
625 "JS" => $assets["JS"],
626 "BUNDLE_JS" => $assets["BUNDLE_JS"],
627 "BUNDLE_CSS" => $assets["BUNDLE_CSS"],
628 "STRINGS" => $assets["STRINGS"],
629 ),
630 );
631 }
632
633 $data["static"] .= substr($content, $offset, $area->openTagStart - $offset);
634
635 if ($dynamicArea->getContainerId() === null)
636 {
637 $data["static"] .=
638 '<div id="bxdynamic_'.$area->id.'_start" style="display:none"></div>'.
639 $dynamicArea->getStub().
640 '<div id="bxdynamic_'.$area->id.'_end" style="display:none"></div>';
641 }
642 else
643 {
644 $data["static"] .= $dynamicArea->getStub();
645 }
646
647 $offset = $area->closingTagEnd;
648 }
649
650 $data["static"] .= substr($content, $offset);
651 }
652 else
653 {
654 $data["static"] = $content;
655 }
656
657 self::replaceSessid($data["static"]);
658 Asset::getInstance()->moveJsToBody($data["static"]);
659
660 $data["md5"] = md5($data["static"]);
661
662 return $data;
663 }
664
670 private static function getFrameIndexes($content)
671 {
672 $openTag = "<!--'start_frame_cache_";
673 $closingTag = "<!--'end_frame_cache_";
674 $ending = "'-->";
675
676 $areas = array();
677 $offset = 0;
678 while (($openTagStart = strpos($content, $openTag, $offset)) !== false)
679 {
680 $endingPos = strpos($content, $ending, $openTagStart);
681 if ($endingPos === false)
682 {
683 break;
684 }
685
686 $idStart = $openTagStart + mb_strlen($openTag);
687 $idLength = $endingPos - $idStart;
688 $areaId = substr($content, $idStart, $idLength);
689 $openTagEnd = $endingPos + mb_strlen($ending);
690
691 $realClosingTag = $closingTag.$areaId.$ending;
692 $closingTagStart = strpos($content, $realClosingTag, $openTagEnd);
693 if ($closingTagStart === false)
694 {
695 $offset = $openTagEnd;
696 continue;
697 }
698
699 $closingTagEnd = $closingTagStart + mb_strlen($realClosingTag);
700
701 $area = new \stdClass();
702 $area->id = $areaId;
703 $area->openTagStart = $openTagStart;
704 $area->openTagEnd = $openTagEnd;
705 $area->closingTagStart = $closingTagStart;
706 $area->closingTagEnd = $closingTagEnd;
707 $areas[] = $area;
708
709 $offset = $closingTagEnd;
710 }
711
712 return !empty($areas) ? $areas : false;
713 }
714
715 private static function getPageBlocks()
716 {
717 $blocks = array();
718 $json = Context::getCurrent()->getServer()->get("HTTP_BX_CACHE_BLOCKS");
719 if ($json !== null && $json <> '')
720 {
721 $blocks = json_decode($json, true);
722 if ($blocks === null)
723 {
724 $blocks = array();
725 }
726 }
727
728 return $blocks;
729 }
730
736 private static function replaceSessid(&$content)
737 {
738 $methodInvocations = bitrix_sessid_post("sessid", true);
739 if ($methodInvocations > 0)
740 {
741 $content = str_replace("value=\"".bitrix_sessid()."\"", "value=\"\"", $content);
742 }
743 }
744
751 public static function onBeforeRestartBuffer()
752 {
753 self::$isBufferRestarted = true;
754 self::setEnable(false);
755
756 Logger::log(
757 array(
758 "TYPE" => Logger::TYPE_BUFFER_RESTART,
759 "MESSAGE" =>
760 "Script: ".
761 ($_SERVER["REAL_FILE_PATH"] ?? $_SERVER["SCRIPT_NAME"])
762 )
763 );
764 }
765
766 public static function onBeforeLocalRedirect(&$url, $skip_security_check, $isExternal)
767 {
768 if (!self::isAjaxRequest() || ($isExternal && $skip_security_check !== true))
769 {
770 return;
771 }
772
773 $response = array(
774 "error" => true,
775 "reason" => "redirect",
776 "redirect_url" => $url,
777 );
778
779 self::setEnable(false);
780
781 Logger::log(
782 array(
783 "TYPE" => Logger::TYPE_LOCAL_REDIRECT,
784 "MESSAGE" =>
785 "Script: ".
786 ($_SERVER["REAL_FILE_PATH"] ?? $_SERVER["SCRIPT_NAME"])."\n".
787 "Redirect Url: ".$url
788 )
789 );
790
791 self::$isRedirect = true;
792 Page::getInstance()->delete();
793
794 $response = new Response\Json($response);
795 $response->addHeader('X-Bitrix-Composite', 'Ajax (error:redirect)');
796
797 $bxRandom = Helper::getAjaxRandom();
798 if ($bxRandom !== false)
799 {
800 $response->addHeader('BX-RAND', $bxRandom);
801 }
802
803 Application::getInstance()->end(0, $response);
804 }
805
806 private static function ensureFileQuota($requiredFreeSpace = 0)
807 {
808 static $tries = 2;
809 if (Helper::checkQuota($requiredFreeSpace) || $tries <= 0)
810 {
811 return;
812 }
813
814 $records = PageTable::getList(
815 array(
816 "select" => array("ID", "CACHE_KEY"),
817 "order" => array("LAST_VIEWED" => "ASC", "ID" => "ASC"),
818 "limit" => self::getDeletionLimit()
819 )
820 );
821
822 $ids = array();
823 $compositeOptions = Helper::getOptions();
824 $deletedSize = 0.0;
825 while ($record = $records->fetch())
826 {
827 $ids[] = $record["ID"];
828 $fileStorage = new Data\FileStorage($record["CACHE_KEY"], array(), $compositeOptions);
829 $deletedSize += doubleval($fileStorage->delete());
830 }
831
832 PageTable::deleteBatch(array("ID" => $ids));
833
834 Helper::updateCacheFileSize(-$deletedSize);
835
836 Logger::log(array(
837 "TYPE" => Logger::TYPE_CACHE_RESET,
838 "MESSAGE" =>
839 "Pages: ".count($ids)."\n".
840 "Size: ".\CFile::formatSize($deletedSize)
841 ));
842
843 if (!Helper::checkQuota($requiredFreeSpace))
844 {
845 $tries--;
846 self::ensureFileQuota($requiredFreeSpace);
847 }
848 }
849
853 private static function getDeletionLimit()
854 {
855 $options = Helper::getOptions();
856 if (isset($options["PAGE_DELETION_LIMIT"]) && intval($options["PAGE_DELETION_LIMIT"]) > 0)
857 {
858 return intval($options["PAGE_DELETION_LIMIT"]);
859 }
860 else
861 {
862 return self::PAGE_DELETION_LIMIT;
863 }
864 }
865
866 private static function getAjaxError($errorMsg = null)
867 {
868 $error = "unknown";
869 if ($errorMsg !== null)
870 {
871 $error = $errorMsg;
872 }
873 elseif (self::$isBufferRestarted)
874 {
875 $error = "buffer_restarted";
876 }
877 elseif (!self::isEnabled())
878 {
879 $error = "not_enabled";
880 }
881 elseif (defined("BX_BUFFER_SHUTDOWN"))
882 {
883 $error = "php_shutdown";
884 }
885 elseif (!Page::getInstance()->isCacheable())
886 {
887 $error = "not_cacheable";
888 }
889 elseif (!self::$isCompositeInjected)
890 {
891 $error = "not_injected";
892 }
893
894 header("X-Bitrix-Composite: Ajax (error:".$error.")");
895 self::sendRandHeader();
896
897 $response = array(
898 "error" => true,
899 "reason" => $error,
900 );
901
902 return \CUtil::PhpToJSObject($response);
903 }
904
908 private static function sendRandHeader()
909 {
910 $bxRandom = Helper::getAjaxRandom();
911 if ($bxRandom !== false)
912 {
913 header("BX-RAND: ".$bxRandom);
914 }
915 }
916
925 private static function getInjectedJs($params = array())
926 {
927 $vars = \CUtil::PhpToJSObject($params);
928
929 $inlineJS = <<<JS
930 (function(w, d) {
931
932 var v = w.frameCacheVars = $vars;
933 var inv = false;
934 if (v.AUTO_UPDATE === false)
935 {
936 if (v.AUTO_UPDATE_TTL && v.AUTO_UPDATE_TTL > 0)
937 {
938 var lm = Date.parse(d.lastModified);
939 if (!isNaN(lm))
940 {
941 var td = new Date().getTime();
942 if ((lm + v.AUTO_UPDATE_TTL * 1000) >= td)
943 {
944 w.frameRequestStart = false;
945 w.preventAutoUpdate = true;
946 return;
947 }
948 inv = true;
949 }
950 }
951 else
952 {
953 w.frameRequestStart = false;
954 w.preventAutoUpdate = true;
955 return;
956 }
957 }
958
959 var r = w.XMLHttpRequest ? new XMLHttpRequest() : (w.ActiveXObject ? new w.ActiveXObject("Microsoft.XMLHTTP") : null);
960 if (!r) { return; }
961
962 w.frameRequestStart = true;
963
964 var m = v.CACHE_MODE; var l = w.location; var x = new Date().getTime();
965 var q = "?bxrand=" + x + (l.search.length > 0 ? "&" + l.search.substring(1) : "");
966 var u = l.protocol + "//" + l.host + l.pathname + q;
967
968 r.open("GET", u, true);
969 r.setRequestHeader("BX-ACTION-TYPE", "get_dynamic");
970 r.setRequestHeader("X-Bitrix-Composite", "get_dynamic");
971 r.setRequestHeader("BX-CACHE-MODE", m);
972 r.setRequestHeader("BX-CACHE-BLOCKS", v.dynamicBlocks ? JSON.stringify(v.dynamicBlocks) : "");
973 if (inv)
974 {
975 r.setRequestHeader("BX-INVALIDATE-CACHE", "Y");
976 }
977
978 try { r.setRequestHeader("BX-REF", d.referrer || "");} catch(e) {}
979
980 if (m === "APPCACHE")
981 {
982 r.setRequestHeader("BX-APPCACHE-PARAMS", JSON.stringify(v.PARAMS));
983 r.setRequestHeader("BX-APPCACHE-URL", v.PAGE_URL ? v.PAGE_URL : "");
984 }
985
986 r.onreadystatechange = function() {
987 if (r.readyState != 4) { return; }
988 var a = r.getResponseHeader("BX-RAND");
989 var b = w.BX && w.BX.frameCache ? w.BX.frameCache : false;
990 if (a != x || !((r.status >= 200 && r.status < 300) || r.status === 304 || r.status === 1223 || r.status === 0))
991 {
992 var f = {error:true, reason:a!=x?"bad_rand":"bad_status", url:u, xhr:r, status:r.status};
993 if (w.BX && w.BX.ready && b)
994 {
995 BX.ready(function() {
996 setTimeout(function(){
997 BX.onCustomEvent("onFrameDataRequestFail", [f]);
998 }, 0);
999 });
1000 }
1001
1002 w.frameRequestFail = f;
1003
1004 return;
1005 }
1006
1007 if (b)
1008 {
1009 b.onFrameDataReceived(r.responseText);
1010 if (!w.frameUpdateInvoked)
1011 {
1012 b.update(false);
1013 }
1014 w.frameUpdateInvoked = true;
1015 }
1016 else
1017 {
1018 w.frameDataString = r.responseText;
1019 }
1020 };
1021
1022 r.send();
1023
1024 var p = w.performance;
1025 if (p && p.addEventListener && p.getEntries && p.setResourceTimingBufferSize)
1026 {
1027 var e = 'resourcetimingbufferfull';
1028 var h = function() {
1029 if (w.BX && w.BX.frameCache && w.BX.frameCache.frameDataInserted)
1030 {
1031 p.removeEventListener(e, h);
1032 }
1033 else
1034 {
1035 p.setResourceTimingBufferSize(p.getEntries().length + 50);
1036 }
1037 };
1038 p.addEventListener(e, h);
1039 }
1040
1041 })(window, document);
1042JS;
1043
1044 $html = "";
1045 if (self::isBannerEnabled())
1046 {
1047 $html .= '<style type="text/css">'.str_replace(array("\n", "\t"), "", self::getInjectedCSS())."</style>\n";
1048 }
1049
1050 $html .= '<script type="text/javascript" data-skip-moving="true">'.
1051 str_replace(array("\n", "\t"), "", $inlineJS).
1052 "</script>";
1053
1054 return $html;
1055 }
1056
1063 public static function getInjectedCSS()
1064 {
1067 return <<<CSS
1068
1069 .bx-composite-btn {
1070 background: url(/bitrix/images/main/composite/sprite-1x.png) no-repeat right 0 #e94524;
1071 border-radius: 15px;
1072 color: #fff !important;
1073 display: inline-block;
1074 line-height: 30px;
1075 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
1076 font-size: 12px !important;
1077 font-weight: bold !important;
1078 height: 31px !important;
1079 padding: 0 42px 0 17px !important;
1080 vertical-align: middle !important;
1081 text-decoration: none !important;
1082 }
1083
1084 @media screen
1085 and (min-device-width: 1200px)
1086 and (max-device-width: 1600px)
1087 and (-webkit-min-device-pixel-ratio: 2)
1088 and (min-resolution: 192dpi) {
1089 .bx-composite-btn {
1090 background-image: url(/bitrix/images/main/composite/sprite-2x.png);
1091 background-size: 42px 124px;
1092 }
1093 }
1094
1095 .bx-composite-btn-fixed {
1096 position: absolute;
1097 top: -45px;
1098 right: 15px;
1099 z-index: 10;
1100 }
1101
1102 .bx-btn-white {
1103 background-position: right 0;
1104 color: #fff !important;
1105 }
1106
1107 .bx-btn-black {
1108 background-position: right -31px;
1109 color: #000 !important;
1110 }
1111
1112 .bx-btn-red {
1113 background-position: right -62px;
1114 color: #555 !important;
1115 }
1116
1117 .bx-btn-grey {
1118 background-position: right -93px;
1119 color: #657b89 !important;
1120 }
1121
1122 .bx-btn-border {
1123 border: 1px solid #d4d4d4;
1124 height: 29px !important;
1125 line-height: 29px !important;
1126 }
1127
1128 .bx-composite-loading {
1129 display: block;
1130 width: 40px;
1131 height: 40px;
1132 background: url(/bitrix/images/main/composite/loading.gif);
1133 }
1134CSS;
1135 }
1136
1143 public static function shouldBeEnabled()
1144 {
1145 if (self::isSelfHostedPortal())
1146 {
1147 return;
1148 }
1149
1150 if (defined("USE_HTML_STATIC_CACHE") && USE_HTML_STATIC_CACHE === true)
1151 {
1152 if (
1153 !defined("BX_SKIP_SESSION_EXPAND") &&
1154 (!defined("ADMIN_SECTION") || (defined("ADMIN_SECTION") && ADMIN_SECTION != "Y"))
1155 )
1156 {
1157 if (self::isInvalidationRequest())
1158 {
1159 $cacheKey = Helper::convertUriToPath(
1163 );
1164
1165 if (!Locker::lock($cacheKey))
1166 {
1167 die(Engine::getAjaxError("invalidation_request_locked"));
1168 }
1169 }
1170
1171 self::setUseHTMLCache();
1172
1173 $options = Helper::getOptions();
1174 if (isset($options["AUTO_UPDATE"]) && $options["AUTO_UPDATE"] === "N")
1175 {
1176 self::setAutoUpdate(false);
1177 }
1178
1179 if (isset($options["AUTO_UPDATE_TTL"]))
1180 {
1181 self::setAutoUpdateTTL($options["AUTO_UPDATE_TTL"]);
1182 }
1183
1184 define("BX_SKIP_SESSION_EXPAND", true);
1185 }
1186 }
1187 else if (Responder::getLastError() !== null && Logger::isOn())
1188 {
1189 $result = Logger::log(array(
1190 "TYPE" => Responder::getLastError(),
1191 "MESSAGE" => Responder::getLastErrorMessage()
1192 ));
1193
1194 //try to update page title on the end of a page execution
1195 if ($result && $result->getId())
1196 {
1197 $recordId = $result->getId();
1198 $eventManager = EventManager::getInstance();
1199 $eventManager->addEventHandler("main", "OnEpilog", function() use($recordId) {
1200 if (is_object($GLOBALS["APPLICATION"]))
1201 {
1202 Debug\Model\LogTable::update($recordId, array(
1203 "TITLE" => $GLOBALS["APPLICATION"]->getTitle()
1204 ));
1205 }
1206 });
1207 }
1208 }
1209
1210 if (
1211 (defined("ENABLE_HTML_STATIC_CACHE_JS") && ENABLE_HTML_STATIC_CACHE_JS === true) &&
1212 (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true)
1213 )
1214 {
1215 \CJSCore::init(array("fc")); //to warm up localStorage
1216 }
1217
1218
1219 }
1220
1228 public static function checkAdminPanel()
1229 {
1230 if (
1231 $GLOBALS["APPLICATION"]->showPanelWasInvoked === true &&
1232 self::getUseHTMLCache() &&
1233 !self::isAjaxRequest() &&
1234 \CTopPanel::shouldShowPanel()
1235 )
1236 {
1237 self::setEnable(false);
1238
1239 Logger::log(
1240 array(
1241 "TYPE" => Logger::TYPE_ADMIN_PANEL,
1242 )
1243 );
1244 }
1245 }
1246
1251 public static function isSelfHostedPortal()
1252 {
1253 if (Configuration::getValue("force_enable_self_hosted_composite") === true)
1254 {
1255 return false;
1256 }
1257 else
1258 {
1259 return ModuleManager::isModuleInstalled("intranet") && !ModuleManager::isModuleInstalled("bitrix24");
1260 }
1261 }
1262
1263 public static function install($setDefaults = true)
1264 {
1265 $eventManager = EventManager::getInstance();
1266
1267 $eventManager->registerEventHandler("main", "OnEpilog", "main", "\\".__CLASS__, "onEpilog");
1268 $eventManager->registerEventHandler("main", "OnLocalRedirect", "main", "\\".__CLASS__, "onEpilog");
1269 $eventManager->registerEventHandler("main", "OnChangeFile", "main", "\\".__CLASS__, "onChangeFile");
1270
1271 //For very first run we have to fall into defaults
1272 if ($setDefaults === true)
1273 {
1275 }
1276
1277 $file = new File(Helper::getEnabledFilePath());
1278 if (!$file->isExists())
1279 {
1280 $file->putContents("");
1281 }
1282 }
1283
1284 public static function uninstall()
1285 {
1286 $eventManager = EventManager::getInstance();
1287
1288 $eventManager->unRegisterEventHandler("main", "OnEpilog", "main", "\\".__CLASS__, "onEpilog");
1289 $eventManager->unRegisterEventHandler("main", "OnLocalRedirect", "main", "\\".__CLASS__, "onEpilog");
1290 $eventManager->unRegisterEventHandler("main", "OnChangeFile", "main", "\\".__CLASS__, "onChangeFile");
1291
1292 $file = new File(Helper::getEnabledFilePath());
1293 $file->delete();
1294 }
1295
1301 public static function isOn()
1302 {
1303 return Helper::isOn();
1304 }
1305
1311 public static function onEpilog()
1312 {
1313 if (!self::isOn())
1314 {
1315 return;
1316 }
1317
1318 global $USER, $APPLICATION;
1319
1320 if (is_object($USER) && $USER->IsAuthorized())
1321 {
1322 if (self::isCurrentUserCC())
1323 {
1324 if ($APPLICATION->get_cookie("CC") !== "Y" || $APPLICATION->get_cookie("NCC") === "Y")
1325 {
1326 self::setCC();
1327 }
1328 }
1329 else
1330 {
1331 if ($APPLICATION->get_cookie("NCC") !== "Y" || $APPLICATION->get_cookie("CC") === "Y")
1332 {
1333 self::setNCC();
1334 }
1335 }
1336 }
1337 else
1338 {
1339 if ($APPLICATION->get_cookie("NCC") === "Y" || $APPLICATION->get_cookie("CC") === "Y")
1340 {
1341 self::deleteCompositeCookies();
1342 }
1343 }
1344
1345 if (\Bitrix\Main\Data\Cache::shouldClearCache())
1346 {
1347 $server = Context::getCurrent()->getServer();
1348
1349 $queryString = DeleteParam(
1350 array(
1351 "clear_cache",
1352 "clear_cache_session",
1353 "bitrix_include_areas",
1354 "back_url_admin",
1355 "show_page_exec_time",
1356 "show_include_exec_time",
1357 "show_sql_stat",
1358 "bitrix_show_mode",
1359 "show_link_stat",
1360 "login"
1361 )
1362 );
1363
1364 $uri = new Uri($server->getRequestUri());
1365 $refinedUri = $queryString != "" ? $uri->getPath()."?".$queryString : $uri->getPath();
1366
1367 $cacheStorage = new Page($refinedUri, Helper::getHttpHost());
1368 $cacheStorage->delete();
1369 }
1370 }
1371
1379 public static function onChangeFile($path, $site)
1380 {
1381 $domains = Helper::getDomains();
1382 $bytes = 0.0;
1383 foreach ($domains as $domain)
1384 {
1385 $cacheStorage = new Page($path, $domain);
1386 $cacheStorage->delete();
1387 }
1388
1389 Helper::updateQuota(-$bytes);
1390 }
1391
1395 public static function onUserLogin()
1396 {
1397 if (!self::isOn())
1398 {
1399 return;
1400 }
1401
1402 if (self::isCurrentUserCC())
1403 {
1404 self::setCC();
1405 }
1406 else
1407 {
1408 self::setNCC();
1409 }
1410 }
1411
1415 public static function onUserLogout()
1416 {
1417 if (self::isOn())
1418 {
1419 self::deleteCompositeCookies();
1420 }
1421 }
1422
1423 public static function isCurrentUserCC()
1424 {
1425 global $USER;
1426 $options = Helper::getOptions();
1427
1428 $groups = isset($options["GROUPS"]) && is_array($options["GROUPS"]) ? $options["GROUPS"] : array();
1429 $groups[] = "2";
1430
1431 $diff = array_diff($USER->GetUserGroupArray(), $groups);
1432
1433 return empty($diff);
1434 }
1435
1439 public static function setNCC()
1440 {
1441 global $APPLICATION;
1442 $APPLICATION->set_cookie("NCC", "Y");
1443 $APPLICATION->set_cookie("CC", "", 0);
1445 }
1446
1450 public static function setCC()
1451 {
1452 global $APPLICATION;
1453 $APPLICATION->set_cookie("CC", "Y");
1454 $APPLICATION->set_cookie("NCC", "", 0);
1455
1456 $page = Page::getInstance();
1457 $page->setUserPrivateKey();
1458 }
1459
1463 public static function deleteCompositeCookies()
1464 {
1465 global $APPLICATION;
1466 $APPLICATION->set_cookie("NCC", "", 0);
1467 $APPLICATION->set_cookie("CC", "", 0);
1469 }
1470
1471 //region Deprecated Methods
1472
1481 public static function setPreventAutoUpdate($preventAutoUpdate = true)
1482 {
1483 self::$autoUpdate = !$preventAutoUpdate;
1484 }
1485
1492 public static function getPreventAutoUpdate()
1493 {
1494 return !self::$autoUpdate;
1495 }
1496
1503 public function getDynamicIDs()
1504 {
1506 }
1507
1515 public function getCurrentDynamicId()
1516 {
1518 }
1519
1535 public function addDynamicData(
1536 $id, $content, $stub = "", $containerId = null, $useBrowserStorage = false,
1537 $autoUpdate = true, $useAnimation = false
1538 )
1539 {
1540 $area = new StaticArea($id);
1541 $area->setStub($stub);
1542 $area->setContainerId($containerId);
1543 $area->setBrowserStorage($useBrowserStorage);
1544 $area->setAutoUpdate($autoUpdate);
1545 $area->setAnimation($useAnimation);
1547 }
1548
1558 public function startDynamicWithID($id)
1559 {
1560 $dynamicArea = new StaticArea($id);
1561
1562 return $dynamicArea->startDynamicArea();
1563 }
1564
1580 public function finishDynamicWithID(
1581 $id, $stub = "", $containerId = null, $useBrowserStorage = false,
1582 $autoUpdate = true, $useAnimation = false)
1583 {
1584 $curDynamicArea = StaticArea::getCurrentDynamicArea();
1585 if ($curDynamicArea === null || $curDynamicArea->getId() !== $id)
1586 {
1587 return false;
1588 }
1589
1590 $curDynamicArea->setStub($stub);
1591 $curDynamicArea->setContainerId($containerId);
1592 $curDynamicArea->setBrowserStorage($useBrowserStorage);
1593 $curDynamicArea->setAutoUpdate($autoUpdate);
1594 $curDynamicArea->setAnimation($useAnimation);
1595
1596 return $curDynamicArea->finishDynamicArea();
1597 }
1598
1599 //endregion
1600}
1601
1602if (!class_exists("Bitrix\\Main\\Page\\Frame", false))
1603{
1604 class_alias("Bitrix\\Main\\Composite\\Engine", "Bitrix\\Main\\Page\\Frame");
1605}
static install($setDefaults=true)
Definition engine.php:1263
static onBeforeLocalRedirect(&$url, $skip_security_check, $isExternal)
Definition engine.php:766
static setPreventAutoUpdate($preventAutoUpdate=true)
Definition engine.php:1481
static setAutoUpdateTTL($ttl)
Definition engine.php:240
finishDynamicWithID( $id, $stub="", $containerId=null, $useBrowserStorage=false, $autoUpdate=true, $useAnimation=false)
Definition engine.php:1580
addDynamicData( $id, $content, $stub="", $containerId=null, $useBrowserStorage=false, $autoUpdate=true, $useAnimation=false)
Definition engine.php:1535
static setUseHTMLCache($useHTMLCache=true)
Definition engine.php:172
static onChangeFile($path, $site)
Definition engine.php:1379
static endBuffering(&$originalContent, $compositeContent)
Definition engine.php:401
static startBuffering($content)
Definition engine.php:332
static setUseAppCache($useAppCache=true)
Definition engine.php:143
static setAutoUpdate($flag)
Definition engine.php:219
static setEnable($isEnabled=true)
Definition engine.php:82
static convertUriToPath($uri, $host=null, $privateKey=null)
Definition helper.php:287
static getHttpHost($host=null)
Definition helper.php:41
static getRealPrivateKey($privateKey=null, $postfix=null)
Definition helper.php:92
static updateQuota($bytes)
Definition helper.php:811
static setOptions($arOptions=array())
Definition helper.php:392
static addDynamicArea(StaticArea $area)
static getCurrent()
Definition context.php:241
static loadMessages($file)
Definition loc.php:64
static isModuleInstalled($moduleName)
$GLOBALS['____1444769544']
Definition license.php:1