cookie; $config = Conf::getInstance(); $defaultParam = array('s' => $ctx->shop->id); if (isset($cookie->iso_code_country)) { $defaultParam['ct'] = $cookie->iso_code_country; } if (isset($cookie->id_currency)) { $defaultParam['c'] = $cookie->id_currency; } if (isset($cookie->id_lang)) { $defaultParam['l'] = $cookie->id_lang; } if ($config->get(Conf::CFG_DIFFMOBILE)) { $defaultParam['mobi'] = $ctx->getMobileDevice() ? 1 : 0; } self::$internal['pub_ttl'] = $config->get(Conf::CFG_PUBLIC_TTL); self::$internal['priv_ttl'] = $config->get(Conf::CFG_PRIVATE_TTL); $unique = str_split(md5(_PS_ROOT_DIR_)); $prefix = 'PS' . implode('', array_slice($unique, 0, 5)); // take 5 char self::$internal['tag_prefix'] = $prefix; self::$internal['cache_dir'] = _PS_CACHE_DIR_ . LiteSpeedCache::MODULE_NAME; $tag0 = $prefix; // for purge all PS cache $tag1 = $prefix . '_' . Conf::TAG_PREFIX_SHOP . $defaultParam['s']; // for purge one shop self::$internal['tag_shared_pub'] = $tag0 . ',' . $tag1; self::$internal['tag_shared_priv'] = 'public:' . $prefix . '_PRIV'; // in private cache, use public:prefix_PRIV if (LiteSpeedCache::canInjectEsi() || LiteSpeedCache::isCacheable() || LiteSpeedCache::isEsiRequest()) { // For some purge events, it may not load from dispatcher, getModuleLink will fail $esiurl = $ctx->link->getModuleLink(LiteSpeedCache::MODULE_NAME, 'esi', $defaultParam); self::$internal['esi_base_url'] = self::getRelativeUri($esiurl); self::$internal['cache_entry'] = $prefix . md5(self::$internal['esi_base_url']); } } public static function getRelativeUri($url) { if (($pos0 = strpos($url, '://')) !== false) { $pos0 += 4; if ($pos1 = strpos($url, '/', $pos0)) { $newurl = Tools::substr($url, $pos1); return $newurl; } } return false; } public static function getCacheFilePath(&$dir) { $dir = self::getCacheDir(); return $dir . '/' . self::$internal['cache_entry'] . '.data'; } public static function getCacheDir() { if (!isset(self::$internal['cache_dir'])) { self::initInternals(); } $dir = self::$internal['cache_dir']; if (!is_dir($dir)) { mkdir($dir); } return $dir; } public static function clearInternalCache() { $count = 0; $dir = self::getCacheDir(); foreach (scandir($dir) as $entry) { if (preg_match('/\.data$/', $entry)) { @unlink($dir . '/' . $entry); ++$count; } } if (_LITESPEED_DEBUG_ >= LSLog::LEVEL_PURGE_EVENT) { LSLog::log(__FUNCTION__ . "=$count", LSLog::LEVEL_PURGE_EVENT); } } public static function genEsiElements(LiteSpeedCacheEsiItem $item) { if (!isset(self::$internal['esi_base_url'])) { self::initInternals(); } // example // Welcome // // id is base64_encode(json_encode($params)) $url = self::$internal['esi_base_url'] . '&pd=' . urlencode($item->getId()); $tagInline = ''; $tagInclude = ''; $esiconf = $item->getConf(); $ttl = $esiconf->getTTL(); if ($ttl === 0 || $ttl === '0') { $ccInclude = $ccInline = 'no-cache'; } else { $isPrivate = $esiconf->isPrivate(); if ($ttl === '') { $ttl = $isPrivate ? self::$internal['priv_ttl'] : self::$internal['pub_ttl']; } $ccInclude = $isPrivate ? 'private' : 'public'; $ccInclude .= ',no-vary'; // onlyCacheIfEmpty is conditional cache, so in esi:include, make it cacheable, this // will only have one cache copy (no need vary) for public page // esi:inline cacheable will be different based on content if ($esiconf->onlyCacheEmtpy() && $item->getContent() !== '') { $ccInline = 'no-cache'; $ttl = 0; } else { $ccInline = $ccInclude . ',max-age=' . $ttl; } $tagInline = ' cache-tag=\''; // need space in front $tagInline .= $isPrivate ? self::$internal['tag_shared_priv'] : self::$internal['tag_shared_pub']; if ($tag = $esiconf->getTag()) { $tagInline .= ',' . self::$internal['tag_prefix'] . '_' . $tag; } $tagInline .= '\''; if ($esiconf->asVar()) { // only asvar need to show tag $tagInclude = $tagInline . ' as-var=\'1\''; } } $esiInclude = sprintf('', $url, $ccInclude, $tagInclude); $inlineStart = sprintf('', $url, $ccInline, $tagInline); $item->setIncludeInlineTag($esiInclude, $inlineStart, $url, $ttl); } // unique prefix for this PS installation, avoid conflict of multiple installations within same cache root public static function getTagPrefix() { return self::getInternalValue('tag_prefix'); } public static function getCacheEntry() { return self::getInternalValue('cache_entry'); } private static function getInternalValue($field) { if (!isset(self::$internal[$field])) { self::initInternals(); } return self::$internal[$field]; } // validator does not allow to use file_get_contents, so use this workaround. protected static function getFileContent($filepath) { $contents = ''; $len = @filesize($filepath); if ($len) { $h = @fopen($filepath, 'rb'); $contents = @fread($h, $len); @fclose($h); } return $contents; } private static function genHtAccessContent($guestMode, $mobileView) { $ls = array(); $ls[] = '### LITESPEED_CACHE_START - Do not remove this line, LSCache plugin will automatically update it'; $ls[] = '# automatically genereated by LiteSpeedCache plugin: ' . 'https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:cache:lscps'; $ls[] = ''; $ls[] = 'CacheLookup on'; if ($guestMode) { $ls[] = 'RewriteEngine on'; if ($mobileView) { $ls[] = 'RewriteCond %{HTTP_COOKIE} !PrestaShop-'; $ls[] = 'RewriteCond %{HTTP_USER_AGENT} "phone|mobile|android|Opera Mini" [NC]'; $ls[] = 'RewriteRule .* - [E=Cache-Control:vary=guestm]'; $ls[] = 'RewriteCond %{HTTP_COOKIE} !PrestaShop-'; $ls[] = 'RewriteCond %{HTTP_USER_AGENT} "!(phone|mobile|android|Opera Mini)" [NC]'; $ls[] = 'RewriteRule .* - [E=Cache-Control:vary=guest]'; } else { $ls[] = 'RewriteCond %{HTTP_COOKIE} !PrestaShop-'; $ls[] = 'RewriteRule .* - [E=Cache-Control:vary=guest]'; } } $ls[] = ''; $ls[] = '### LITESPEED_CACHE_END'; $newcontent = implode("\n", $ls) . "\n"; return $newcontent; } public static function htAccessBackup($suffix) { $path = _PS_ROOT_DIR_ . '/.htaccess'; $newfile = $path . '.' . $suffix . time(); if (!file_exists($newfile)) { $content = self::getFileContent($path); if ($content) { $res = file_put_contents($newfile, $content); if ($res) { LSLog::log(__FUNCTION__ . ' backed up as ' . $newfile, LSLog::LEVEL_UPDCONFIG); return true; } } } return false; } public static function htAccessUpdate($enableCache, $guestMode, $mobileView) { $path = _PS_ROOT_DIR_ . '/.htaccess'; $oldlines = file($path); if ($oldlines === '') { LSLog::log(__FUNCTION__ . ' please manually fix .htaccess, may due to permission', LSLog::LEVEL_FORCE); return false; } $newlines = array(); $ind = false; // always remove first foreach ($oldlines as $line) { if (!$ind) { if (strpos($line, 'LITESPEED_CACHE_START') || stripos($line, 'IfModule LiteSpeed')) { $ind = true; } else { $newlines[] = $line; } } else { if (strpos($line, 'LITESPEED_CACHE_END')) { $ind = false; } elseif (strpos($line, '~~start~~')) { $ind = false; $newlines[] = $line; } } } $newcontent = ''; if ($enableCache) { $newcontent = self::genHtAccessContent($guestMode, $mobileView); } $newcontent .= implode('', $newlines); $res = file_put_contents($path, $newcontent); if ($res) { LSLog::log(__FUNCTION__ . ' updated', LSLog::LEVEL_UPDCONFIG); return true; } else { LSLog::log(__FUNCTION__ . ' cannot save! Please manually fix .htaccess file', LSLog::LEVEL_FORCE); return false; } } // if id is false, load all public static function getRelatedItems($id) { $items = array(); $dir = ''; $cacheFile = self::getCacheFilePath($dir); $snapshot = self::getFileContent($cacheFile); $saved = json_decode($snapshot, true); if (!is_array($saved) || json_last_error() !== JSON_ERROR_NONE || ($id != false && !isset($saved['data'][$id]))) { return $items; } $related = array(); $tag = ($id) ? $saved['data'][$id]['tag'] : Conf::TAG_ENV; if ($tag == Conf::TAG_ENV) { $related = array_keys($saved['data']); } elseif (isset($saved['tags'][$tag])) { $related = array_keys($saved['tags'][$tag]); } foreach ($related as $rid) { if ($rid != $id) { $ri = LiteSpeedCacheEsiItem::newFromSavedData($saved['data'][$rid]); if ($ri != null) { $items[] = $ri; } } } return $items; } public static function syncItemCache($itemList) { $dir = ''; $cacheFile = self::getCacheFilePath($dir); $snapshot = self::getFileContent($cacheFile); $saved = json_decode($snapshot, true); if (!is_array($saved) || json_last_error() !== JSON_ERROR_NONE) { $saved = array('data' => array(), 'tags' => array()); } foreach ($itemList as $item) { $id = $item->getId(); $descr = $item->getInfoLog(true); $saved['data'][$id] = $item; $tag = $item->getTag(); if ($tag == Conf::TAG_ENV) { continue; } if (!isset($saved['tags'][$tag])) { $saved['tags'][$tag] = array($id => $descr); } elseif (!isset($saved['tags'][$tag][$id])) { $saved['tags'][$tag][$id] = $descr; } } ksort($saved['data']); ksort($saved['tags']); $newsnapshot = json_encode($saved, JSON_UNESCAPED_SLASHES); if ($snapshot != $newsnapshot) { if (_LITESPEED_DEBUG_ >= LSLog::LEVEL_SAVED_DATA) { LSLog::log(__FUNCTION__ . ' updated data ' . var_export($saved, true), LSLog::LEVEL_SAVED_DATA); } file_put_contents($cacheFile, $newsnapshot); } } public static function isStaticResource($url) { $pattern = '/(js|css|jpg|png|svg|gif|woff|woff2)$/'; return preg_match($pattern, $url); } public static function licenseEnabled() { // possible string "on,crawler,esi", will enforce checking in future return ((isset($_SERVER['X-LSCACHE']) && $_SERVER['X-LSCACHE']) // for lsws || (isset($_SERVER['HTTP_X_LSCACHE']) && $_SERVER['HTTP_X_LSCACHE'])); // lslb } }