Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
appcache.php
1<?php
3
5use Bitrix\Main\Page\Asset;
6
8{
9
10 const MANIFEST_CHECK_FILE = "/bitrix/tools/check_appcache.php";
11 const DEBUG_HOLDER = "//__APP_CACHE_DEBUG_HOLDER__";
12 private static $debug;
13 private static $instance;
14 private static $isEnabled = false;
15 private static $customCheckFile = null;
16 private $files = Array();
17 private $pageURI = "";
18 private $network = Array();
19 private $fallbackPages = Array();
20 private $params = Array();
21 private $isSided = false;
22 private $isModified = false;
23 private $receivedManifest = "";
24 private $excludeImagePatterns= array();
25
26 private $receivedCacheParams = Array();
27
28 private function __construct()
29 {
30 //use \Bitrix\Main\Composite\AppCache::getInstance();
31 }
32
36 public static function getDebug()
37 {
38 return self::$debug;
39 }
40
44 public function isEnabled()
45 {
46 return self::$isEnabled;
47 }
48
53 public function getExcludeImagePatterns()
54 {
55 return $this->excludeImagePatterns;
56 }
57
62 public function setExcludeImagePatterns($excludeImagePatterns)
63 {
64 $this->excludeImagePatterns = $excludeImagePatterns;
65 }
66
67
68 private function __clone()
69 {
70 //you can't clone it
71
72 }
73
74 public static function getInstance()
75 {
76 if (is_null(self::$instance))
77 {
78 self::$instance = new static();
79 self::$debug = (defined("BX_APPCACHE_DEBUG") && BX_APPCACHE_DEBUG);
80 }
81
82 return self::$instance;
83 }
84
90 public static function setEnabled($isEnabled = true)
91 {
92 self::$isEnabled = (bool)$isEnabled;
93
94 }
95
96 public function generate(&$content)
97 {
98 $manifest = static::getInstance();
99 $files = $manifest->getFilesFromContent($content);
100
101 $this->isModified = false;
102 $manifestId = $this->getCurrentManifestID();
103
104 if ($this->isSided)
105 {
106 $curManifestId = $this->getManifestID($this->pageURI, $this->receivedCacheParams);
107 if ($curManifestId != $manifestId)
108 {
109 self::removeManifestById($curManifestId);
110 }
111 }
112
113 $currentHashSum = md5(serialize($files["FULL_FILE_LIST"]) . serialize($this->fallbackPages) . serialize($this->network) . serialize($this->excludeImagePatterns));
114 $manifestCache = $this->readManifestCache($manifestId);
115 if (!$manifestCache || $manifestCache["FILE_HASH"] != $currentHashSum || self::$debug)
116 {
117 $this->isModified = true;
118 $this->setFiles($files["FULL_FILE_LIST"]);
119 $this->setNetworkFiles(Array("*"));
120 $arFields = array(
121 "ID" => $manifestId,
122 "TEXT" => $this->getManifestContent(),
123 "FILE_HASH" => $currentHashSum,
124 "EXCLUDE_PATTERNS_HASH"=> md5(serialize($this->excludeImagePatterns)),
125 "FILE_DATA" => Array(
126 "FILE_TIMESTAMPS" => $files["FILE_TIMESTAMPS"],
127 "CSS_FILE_IMAGES" => $files["CSS_FILE_IMAGES"]
128 )
129 );
130
131 if (!self::$debug)
132 {
133 $this->writeManifestCache($arFields);
134 }
135 else
136 {
137 $jsFields = json_encode($arFields);
138 $fileCount = count($this->files);
139 $params = json_encode($this->params);
140 $fileCountImages = 0;
141 foreach ($arFields["FILE_DATA"]["CSS_FILE_IMAGES"] as $file=>$images)
142 {
143 if (is_array($images))
144 {
145 $fileCountImages += count($images);
146 }
147 }
148
149
150 $debugOutput = <<<JS
151
152 console.log("-------APPLICATION CACHE DEBUG INFO------");
153 console.log("File count:", $fileCount);
154 console.log("Image file count:", $fileCountImages);
155 console.log("Params:", $params);
156 console.log("Detail:", $jsFields);
157 console.log("--------------------------------------------");
158JS;
159
160 $jsContent = str_replace(array("\n", "\t"), "", $debugOutput);
161 $content = str_replace(self::DEBUG_HOLDER, $jsContent, $content);
162 }
163 }
164
165 return $this->getIsModified();
166 }
167
172 public static function onBeforeEndBufferContent()
173 {
174 global $APPLICATION;
175 $selfObject = self::getInstance();
176 $server = \Bitrix\Main\Context::getCurrent()->getServer();
177 $params = Array();
178 $appCacheUrl = $server->get("HTTP_BX_APPCACHE_URL");
179 $appCacheParams = $server->get("HTTP_BX_APPCACHE_PARAMS");
180 if ($appCacheUrl <> '')
181 {
182 //TODO compare $_SERVER["REQUEST_URI"] and $_SERVER["HTTP_BX_APPCACHE_URL"]
183 $selfObject->setIsSided(true);
184 $selfObject->setPageURI($appCacheUrl);
185 if ($appCacheParams)
186 {
187 $params = json_decode($appCacheParams, true);
188
189 if (!is_array($params))
190 {
191 $params = array();
192 }
193
194 $selfObject->setReceivedCacheParams($params);
195 }
196 }
197 else
198 {
199 $selfObject->setPageURI($server->get("REQUEST_URI"));
200
201 if(!self::$debug)
202 {
203 $APPLICATION->SetPageProperty("manifest", " manifest=\"" . self::getManifestCheckFile() . "?manifest_id=" . $selfObject->getCurrentManifestID() . "\"");
204 }
205 else
206 {
207 Asset::getInstance()->addString("<script type=\"text/javascript\">".self::DEBUG_HOLDER."</script>");
208 }
209
210 $params = Array(
211 "PAGE_URL" => $selfObject->getPageURI(),
212 "PARAMS" => $selfObject->getAdditionalParams(),
213 "MODE" => "APPCACHE"
214 );
215 }
216
217 return (is_array($params) ? $params : array());
218 }
219
224 public static function getManifestCheckFile()
225 {
226 $checkFile = self::MANIFEST_CHECK_FILE;
227 if(self::$customCheckFile != null && self::$customCheckFile <> '')
228 $checkFile = self::$customCheckFile;
229 return $checkFile;
230 }
231
237 public function setManifestCheckFile($customManifestCheckFile)
238 {
239 self::$customCheckFile = $customManifestCheckFile;
240 }
241
242 /*
243 * OnEndBufferContent handler
244 */
245 public static function onEndBufferContent(&$content)
246 {
247 static::getInstance()->generate($content);
248 }
249
254 public function getManifestContent()
255 {
256 $manifestText = "CACHE MANIFEST\n\n";
257 $manifestText .= $this->getManifestDescription();
258 $manifestText .= "#files" . "\n\n";
259 $manifestText .= implode("\n", $this->files) . "\n\n";
260 $manifestText .= "NETWORK:\n";
261 $manifestText .= implode("\n", $this->network) . "\n\n";
262 $manifestText .= "FALLBACK:\n\n";
263 $countFallback = count($this->fallbackPages);
264 for ($i = 0; $i < $countFallback; $i++)
265 {
266 $manifestText .= $this->fallbackPages[$i]["online"] . " " . $this->fallbackPages[$i]["offline"] . "\n";
267 }
268
269 return $manifestText;
270 }
271
279 public function getFilesFromContent($content)
280 {
281 $files = Array();
282 $arFilesByType = Array();
283 $arExtensions = Array("js", "css");
284 $extension_regex = "(?:" . implode("|", $arExtensions) . ")";
285 $findImageRegexp = "/
286 ((?i:
287 href=
288 |src=
289 |BX\\.loadCSS\\(
290 |BX\\.loadScript\\(
291 |jsUtils\\.loadJSFile\\(
292 |background\\s*:\\s*url\\(
293 )) #attribute
294 (\"|') #open_quote
295 ([^?'\"]+\\.) #href body
296 (" . $extension_regex . ") #extentions
297 (|\\?\\d+|\\?v=\\d+) #params
298 (\\2) #close_quote
299 /x";
300 $match = Array();
301 preg_match_all($findImageRegexp, $content, $match);
302
303 $link = $match[3];
304 $extension = $match[4];
305 $params = $match[5];
306 $linkCount = count($link);
307 $fileData = array(
308 "FULL_FILE_LIST" => array(),
309 "FILE_TIMESTAMPS" => array(),
310 "CSS_FILE_IMAGES" => array()
311 );
312 for ($i = 0; $i < $linkCount; $i++)
313 {
314 $fileData["FULL_FILE_LIST"][] = $files[] = $link[$i] . $extension[$i] . $params[$i];
315 $fileData["FILE_TIMESTAMPS"][$link[$i] . $extension[$i]] = $params[$i];
316 $arFilesByType[$extension[$i]][] = $link[$i] . $extension[$i];
317 }
318
319 $manifestCache = $this->readManifestCache($this->getCurrentManifestID());
320 $excludePatternsHash = md5(serialize($this->excludeImagePatterns));
321
322 if (array_key_exists("css", $arFilesByType))
323 {
324 $findImageRegexp = '#([;\s:]*(?:url|@import)\s*\‍(\s*)(\'|"|)(.+?)(\2)\s*\‍)#si';
325 if(!empty($this->excludeImagePatterns))
326 {
327 $findImageRegexp = '#([;\s:]*(?:url|@import)\s*\‍(\s*)(\'|"|)((?:(?!'.implode("|",$this->excludeImagePatterns).').)+?)(\2)\s*\‍)#si';
328 }
329
330 $cssCount = count($arFilesByType["css"]);
331 for ($j = 0; $j < $cssCount; $j++)
332 {
333 $cssFilePath = $arFilesByType["css"][$j];
334 if ($manifestCache["FILE_DATA"]["FILE_TIMESTAMPS"][$cssFilePath] != $fileData["FILE_TIMESTAMPS"][$cssFilePath]
335 ||$excludePatternsHash != $manifestCache["EXCLUDE_PATTERNS_HASH"]
336 )
337 {
338
339 $fileContent = false;
340 $fileUrl = parse_url($cssFilePath);
341 $file = new \Bitrix\Main\IO\File(Application::getDocumentRoot() . $fileUrl['path']);
342
343 if($file->getExtension() !== "css")
344 continue;
345
346 if ($file->isExists() && $file->isReadable())
347 {
348 $fileContent = $file->getContents();
349 }
350 elseif ($fileUrl["scheme"])
351 {
352 $req = new \CHTTP();
353 $req->http_timeout = 20;
354 $fileContent = $req->Get($cssFilePath);
355 }
356
357 if ($fileContent != false)
358 {
359 $cssFileRelative = new \Bitrix\Main\IO\File($cssFilePath);
360 $cssPath = $cssFileRelative->getDirectoryName();
361 preg_match_all($findImageRegexp, $fileContent, $match);
362 $matchCount = count($match[3]);
363 for ($k = 0; $k < $matchCount; $k++)
364 {
365
366 $file = self::replaceUrlCSS($match[3][$k], addslashes($cssPath));
367
368 if (!in_array($file, $files) && !mb_strpos($file, ";base64"))
369 {
370 $fileData["FULL_FILE_LIST"][] = $files[] = $file;
371 $fileData["CSS_FILE_IMAGES"][$cssFilePath][] = $file;
372 }
373 }
374 }
375 }
376 else
377 {
378 $fileData["CSS_FILE_IMAGES"][$cssFilePath] = $manifestCache["FILE_DATA"]["CSS_FILE_IMAGES"][$cssFilePath];
379 if (is_array($manifestCache["FILE_DATA"]["CSS_FILE_IMAGES"][$cssFilePath]))
380 {
381 $fileData["FULL_FILE_LIST"] = array_merge($fileData["FULL_FILE_LIST"], $manifestCache["FILE_DATA"]["CSS_FILE_IMAGES"][$cssFilePath]);
382 }
383 }
384
385 }
386 }
387
388 return $fileData;
389 }
390
399 private static function replaceUrlCSS($url, $cssPath)
400 {
401 if (strpos($url, "://") !== false || strpos($url, "data:") !== false)
402 {
403 return $url;
404 }
405 $url = trim(stripslashes($url), "'\" \r\n\t");
406 if (mb_substr($url, 0, 1) == "/")
407 {
408 return $url;
409 }
410
411 return $cssPath . '/' . $url;
412 }
413
418 public function setReceivedCacheParams($receivedCacheParams)
419 {
420 $this->receivedCacheParams = $receivedCacheParams;
421 }
422
427 public function getReceivedCacheParams()
428 {
429 return $this->receivedCacheParams;
430 }
431
437 public function setReceivedManifest($receivedManifest)
438 {
439 $this->receivedManifest = $receivedManifest;
440 }
441
442 public function getReceivedManifest()
443 {
444 return $this->receivedManifest;
445 }
446
447 public function setIsSided($isSided)
448 {
449 $this->isSided = $isSided;
450 }
451
452 public function getIsSided()
453 {
454 return $this->isSided;
455 }
456
457 public function setPageURI($pageURI = "")
458 {
459 $this->pageURI = $pageURI;
460 }
461
462 public function getPageURI()
463 {
464 return $this->pageURI;
465 }
466
467 public function setFiles($arFiles)
468 {
469 if (!empty($this->files))
470 {
471 $this->files = array_merge($this->files, $arFiles);
472 }
473 else
474 {
475 $this->files = $arFiles;
476 }
477 }
478
479 public function addFile($filePath)
480 {
481 $this->files[] = $filePath;
482 }
483
484 public function addAdditionalParam($name, $value)
485 {
486 $this->params[$name] = $value;
487 }
488
489 public function getAdditionalParams()
490 {
491 return $this->params;
492 }
493
494 public function setNetworkFiles($network)
495 {
496 $this->network = $network;
497 }
498
499 public function getNetworkFiles()
500 {
501 return $this->network;
502 }
503
504 public function addFallbackPage($onlinePage, $offlinePage)
505 {
506 $this->fallbackPages[] = Array(
507 "online" => $onlinePage,
508 "offline" => $offlinePage
509 );
510 }
511
512 public function getFallbackPages()
513 {
514 return $this->fallbackPages;
515 }
516
517 public function getCurrentManifestID()
518 {
519 return $this->getManifestID($this->pageURI, $this->params);
520 }
521
522 public function getIsModified()
523 {
524 return $this->isModified && !self::$debug;
525 }
526
527 private function getManifestDescription()
528 {
529
530 $manifestParams = "";
531 $arCacheParams = $this->params;
532 if (!empty($arCacheParams))
533 {
534 foreach ($arCacheParams as $key => $value)
535 {
536 $manifestParams .= "#" . $key . "=" . $value . "\n";
537 }
538 }
539
540 $desc = "#Date: " . date("r") . "\n";
541 $desc .= "#Page: " . $this->pageURI . "\n";
542 $desc .= "#Count: " . count($this->files) . "\n";
543 $desc .= "#Params: \n" . $manifestParams . "\n\n";
544 $desc .= "#Exclude patterns: \n" . "#".implode("\n#",$this->getExcludeImagePatterns()) . "\n\n";
545
546 return $desc;
547 }
548
549 private function writeManifestCache($arFields)
550 {
551 $cache = new \CPHPCache();
552 $manifestId = $arFields["ID"];
553 $this->removeManifestById($manifestId);
554 $cachePath = self::getCachePath($manifestId);
555 $cache->StartDataCache(3600 * 24 * 365, $manifestId, $cachePath);
556 $cache->EndDataCache($arFields);
557
558 return true;
559 }
560
561 public static function readManifestCache($manifestId)
562 {
563 $cache = new \CPHPCache();
564
565 $cachePath = self::getCachePath($manifestId);
566 if ($cache->InitCache(3600 * 24 * 365, $manifestId, $cachePath))
567 {
568 return $cache->getVars();
569 }
570
571 return false;
572 }
573
574 private static function removeManifestById($manifestId)
575 {
576 $cache = new \CPHPCache();
577 $cachePath = self::getCachePath($manifestId);
578
579 return $cache->CleanDir($cachePath);
580 }
581
587 public static function getCachePath($manifestId)
588 {
589 $cachePath = "/appcache/".mb_substr($manifestId, 0, 2)."/".mb_substr($manifestId, 2, 4) . "/";
590
591 return $cachePath;
592 }
593
594
595 private static function getManifestID($pageURI, $arParams)
596 {
597 $id = $pageURI;
598 if (!empty($arParams))
599 {
600 $strCacheParams = "";
601 foreach ($arParams as $key => $value)
602 {
603 $strCacheParams .= $key . "=" . $value;
604 }
605
606 $id .= $strCacheParams;
607 }
608
609 return md5($id);
610 }
611
612 public static function checkObsoleteManifest()
613 {
614 $server = \Bitrix\Main\Context::getCurrent()->getServer();
615 $appCacheUrl = $server->get("HTTP_BX_APPCACHE_URL");
616 $appCacheParams = $server->get("HTTP_BX_APPCACHE_PARAMS");
617 if ($appCacheUrl)
618 {
619 $params = json_decode($appCacheParams, true);
620
621 if (!is_array($params))
622 {
623 $params = array();
624 }
625
626 static::clear($appCacheUrl, $params);
627 }
628 }
629
630 private static function clear($url, $params)
631 {
632 $manifestId = self::getManifestID($url, $params);
633 if (self::readManifestCache($manifestId))
634 {
635 self::removeManifestById($manifestId);
636 self::getInstance()->isModified = true;
637 }
638
639 }
640
641}
642
643class_alias("Bitrix\\Main\\Composite\\AppCache", "Bitrix\\Main\\Data\\AppCacheManifest");
static getCachePath($manifestId)
Definition appcache.php:587
setExcludeImagePatterns($excludeImagePatterns)
Definition appcache.php:62
setManifestCheckFile($customManifestCheckFile)
Definition appcache.php:237
static onEndBufferContent(&$content)
Definition appcache.php:245
setReceivedManifest($receivedManifest)
Definition appcache.php:437
addAdditionalParam($name, $value)
Definition appcache.php:484
addFallbackPage($onlinePage, $offlinePage)
Definition appcache.php:504
static setEnabled($isEnabled=true)
Definition appcache.php:90
setReceivedCacheParams($receivedCacheParams)
Definition appcache.php:418
static readManifestCache($manifestId)
Definition appcache.php:561