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