all == null) { $this->init(); } switch ($configField) { case self::ENTRY_ALL: return array_replace($this->getDefaultConfData(self::ENTRY_ALL), $this->all); // in global scope case self::CFG_ENABLED: case self::CFG_DIFFMOBILE: case self::CFG_GUESTMODE: case self::CFG_NOCACHE_VAR: case self::CFG_NOCACHE_URL: case self::CFG_FLUSH_PRODCAT: // in global developer form case self::CFG_DEBUG: case self::CFG_DEBUG_LEVEL: case self::CFG_ALLOW_IPS: case self::CFG_DEBUG_IPS: if (!isset($this->all[$configField])) { $this->all = array_replace($this->getDefaultConfData(self::ENTRY_ALL), $this->all); } return $this->all[$configField]; // shop level config case self::ENTRY_SHOP: return array_replace($this->getDefaultConfData(self::ENTRY_SHOP), $this->shop); // in shop scope case self::CFG_PUBLIC_TTL: case self::CFG_PRIVATE_TTL: case self::CFG_HOME_TTL: case self::CFG_404_TTL: case self::CFG_DIFFCUSTGRP: if (!isset($this->shop[$configField])) { $this->shop = array_replace($this->getDefaultConfData(self::ENTRY_SHOP), $this->shop); } return $this->shop[$configField]; // in module customization case self::ENTRY_MODULE: return $this->esiModConf['mods']; } return null; } public function overrideGuestMode() { // if config has guest mode, change to first page only // hardcode behavior here as htaccess already set vary if ($this->all[self::CFG_GUESTMODE] == 1) { $this->all[self::CFG_GUESTMODE] = 2; } } public function getArray($configField) { if (($value = $this->get($configField)) != '') { return preg_split("/[\s,]+/", $value, null, PREG_SPLIT_NO_EMPTY); } return array(); } public function getDefaultConfData($key) { if ($key == self::ENTRY_ALL) { return array( self::CFG_ENABLED => 0, self::CFG_DIFFMOBILE => 0, self::CFG_GUESTMODE => 1, self::CFG_NOCACHE_VAR => '', self::CFG_NOCACHE_URL => '', self::CFG_FLUSH_PRODCAT => 0, self::CFG_DEBUG => 0, self::CFG_DEBUG_LEVEL => 9, self::CFG_ALLOW_IPS => '', self::CFG_DEBUG_IPS => '', ); } elseif ($key == self::ENTRY_SHOP) { return array( self::CFG_PUBLIC_TTL => 86400, self::CFG_PRIVATE_TTL => 1800, self::CFG_HOME_TTL => 86400, self::CFG_404_TTL => 86400, self::CFG_DIFFCUSTGRP => 0, ); } } public function getAllConfigValues() { return array_merge($this->get(self::ENTRY_ALL), $this->get(self::ENTRY_SHOP)); } // action is add/edit/delete public function saveModConfigValues($currentEntry, $action) { if (empty($currentEntry['id'])) { return false; } $id = $currentEntry['id']; $item = $this->getEsiModuleConf($id); if ($item != null && !$item->isCustomized()) { // should not come here, do not allow touch default modules return false; } $oldevents = array_keys($this->esiModConf['purge_events']); if ($action == 'new' || $action == 'edit') { $newitem = new EsiConf($id, EsiConf::TYPE_CUSTOMIZED, $currentEntry); $this->esiModConf['mods'][$id] = $newitem; } elseif ($action == 'delete') { unset($this->esiModConf['mods'][$id]); } else { // sth wrong return false; } $newMod = array(); $newevents = array(); foreach ($this->esiModConf['mods'] as $mi) { if (($events = $mi->getPurgeEvents()) != null) { $newevents = array_merge($newevents, $events); } if ($mi->isCustomized()) { $newMod[$mi->getModuleName()] = $mi; } } $newevents = array_unique($newevents); if (!empty($newMod)) { ksort($newMod); } $newModValue = json_encode($newMod); if ($newModValue != $this->custMod) { $this->updateConfiguration(self::ENTRY_MODULE, $newModValue); } $builtin = $this->getReservedHooks(); $added = array_diff($newevents, $oldevents, $builtin); $removed = array_diff($oldevents, $newevents, $builtin); if (!empty($added) || !empty($removed)) { $mymod = Module::getInstanceByName(LiteSpeedCache::MODULE_NAME); foreach ($added as $a) { $res = Hook::registerHook($mymod, $a); if ($this->isDebug >= LSLog::LEVEL_UPDCONFIG) { LSLog::log('in registerHook ' . $a . '=' . $res, LSLog::LEVEL_UPDCONFIG); } } foreach ($removed as $r) { $res = Hook::unregisterHook($mymod, $r); if ($this->isDebug >= LSLog::LEVEL_UPDCONFIG) { LSLog::log('in unregisterHook ' . $r . '=' . $res, LSLog::LEVEL_UPDCONFIG); } } return 2; } return 1; } public function updateConfiguration($key, $values) { if ($this->isDebug >= LSLog::LEVEL_UPDCONFIG) { LSLog::log('in updateConfiguration context=' . Shop::getContext() . " key = $key value = " . var_export($values, true), LSLog::LEVEL_UPDCONFIG); } switch ($key) { case self::ENTRY_ALL: $this->all = array( self::CFG_ENABLED => $values[self::CFG_ENABLED], self::CFG_DIFFMOBILE => $values[self::CFG_DIFFMOBILE], self::CFG_GUESTMODE => $values[self::CFG_GUESTMODE], self::CFG_NOCACHE_VAR => $values[self::CFG_NOCACHE_VAR], self::CFG_NOCACHE_URL => $values[self::CFG_NOCACHE_URL], self::CFG_FLUSH_PRODCAT => $values[self::CFG_FLUSH_PRODCAT], self::CFG_DEBUG => $values[self::CFG_DEBUG], self::CFG_DEBUG_LEVEL => $values[self::CFG_DEBUG_LEVEL], self::CFG_ALLOW_IPS => $values[self::CFG_ALLOW_IPS], self::CFG_DEBUG_IPS => $values[self::CFG_DEBUG_IPS], ); Configuration::updateValue(self::ENTRY_ALL, json_encode($this->all)); break; case self::ENTRY_SHOP: $this->shop = array( self::CFG_PUBLIC_TTL => $values[self::CFG_PUBLIC_TTL], self::CFG_PRIVATE_TTL => $values[self::CFG_PRIVATE_TTL], self::CFG_HOME_TTL => $values[self::CFG_HOME_TTL], self::CFG_404_TTL => $values[self::CFG_404_TTL], self::CFG_DIFFCUSTGRP => $values[self::CFG_DIFFCUSTGRP], ); Configuration::updateValue(self::ENTRY_SHOP, json_encode($this->shop)); break; case self::ENTRY_MODULE: $this->custMod = $values; Configuration::updateValue(self::ENTRY_MODULE, $this->custMod); break; default: return false; } return true; } public function isDebug($requiredLevel = 0) { $this->get(self::CFG_DEBUG); // this will initialize if needed return ($this->isDebug < $requiredLevel) ? 0 : $this->isDebug; } private function init() { $this->all = json_decode(Configuration::get(self::ENTRY_ALL), true); if (!$this->all) { // for config not exist, or decode err LSLog::log('Config not exist yet or decode err', LSLog::LEVEL_FORCE); $this->all = $this->getDefaultConfData(self::ENTRY_ALL); } if ($this->all[self::CFG_DEBUG]) { $ips = $this->getArray(self::CFG_DEBUG_IPS); if (empty($ips) || in_array($_SERVER['REMOTE_ADDR'], $ips)) { $this->isDebug = $this->all[self::CFG_DEBUG_LEVEL]; } } $this->shop = json_decode(Configuration::get(self::ENTRY_SHOP), true); if (!$this->shop) { $this->shop = $this->getDefaultConfData(self::ENTRY_SHOP); } $this->custMod = Configuration::get(self::ENTRY_MODULE); $this->esiModConf = array('mods' => array(), 'purge_events' => array()); $custdata = json_decode($this->custMod, true); if ($custdata) { foreach ($custdata as $name => $sdata) { $esiconf = new EsiConf($name, EsiConf::TYPE_CUSTOMIZED, $sdata); $this->registerEsiModule($esiconf); } } $this->pubController = array( 'IndexController' => self::TAG_HOME, // controller name - tag linked to it 'ProductController' => '', 'CategoryController' => '', 'CmsController' => '', 'ManufacturerController' => '', 'SupplierController' => '', 'SearchController' => self::TAG_SEARCH, 'BestSalesController' => self::TAG_SEARCH, 'NewProductsController' => self::TAG_SEARCH, 'PricesDropController' => self::TAG_SEARCH, 'SitemapController' => self::TAG_SITEMAP, 'StoresController' => self::TAG_STORES, 'PageNotFoundController' => self::TAG_404, ); LSLog::setDebugLevel($this->isDebug); if (!defined('_LITESPEED_DEBUG_')) { define('_LITESPEED_DEBUG_', $this->isDebug); } } public function isControllerCacheable($controllerClass) { if (!isset($this->pubController[$controllerClass])) { return false; } if ($controllerClass == 'PageNotFoundController') { if ($this->get(self::CFG_404_TTL) == 0) { return false; } } return $this->pubController[$controllerClass]; } public function getNoCacheConf() { $nocache = array(self::CFG_NOCACHE_URL => $this->getArray(self::CFG_NOCACHE_URL), self::CFG_NOCACHE_VAR => $this->getArray(self::CFG_NOCACHE_VAR), ); return $nocache; } // if allowed, return conf public function canInjectEsi($name, $params) { $m = &$this->esiModConf['mods']; if (isset($m[$name]) && $m[$name]->canInject($params)) { return $m[$name]; } else { return false; } } public function esiInjectRenderWidget($mName, $hName) { $m = &$this->esiModConf['mods']; if (isset($m[$mName]) && $m[$mName]->injectRenderWidget($hName)) { return $m[$mName]; } return false; } public function isEsiModule($moduleName) { return isset($this->esiModConf['mods'][$moduleName]); } public function getEsiModuleConf($moduleName) { if (isset($this->esiModConf['mods'][$moduleName])) { return $this->esiModConf['mods'][$moduleName]; } return null; } public function getPurgeTagsByEvent($event) { if (isset($this->esiModConf['purge_events'][$event])) { return $this->esiModConf['purge_events'][$event]; } return null; } public function registerEsiModule(EsiConf $esiConf) { $modname = $esiConf->getModuleName(); $this->esiModConf['mods'][$modname] = $esiConf; $isPriv = $esiConf->isPrivate(); $type = $isPriv ? 'priv' : 'pub'; $tag = $esiConf->getTag(); if ($pc = $esiConf->getPurgeControllers()) { foreach ($pc as $cname => $cparam) { // allow ClassName?param1¶m2=value $cname = Tools::strtolower($cname); if (!isset($this->purgeController[$cname])) { $this->purgeController[$cname] = array(); } $detail = &$this->purgeController[$cname]; if (!isset($detail[$cparam])) { $detail[$cparam] = array('priv' => array(), 'pub' => array()); } if (!in_array($tag, $detail[$cparam][$type])) { $detail[$cparam][$type][] = $tag; } } } $events = $esiConf->getPurgeEvents(); if (!empty($events)) { foreach ($events as $event) { if (!isset($this->esiModConf['purge_events'][$event])) { $this->esiModConf['purge_events'][$event] = array('priv' => array(), 'pub' => array()); } if (!in_array($tag, $this->esiModConf['purge_events'][$event][$type])) { $this->esiModConf['purge_events'][$event][$type][] = $tag; } } } } public function isPurgeController($controller_class) { $c = Tools::strtolower($controller_class); if (!isset($this->purgeController[$c])) { return false; } $conf = array('pub' => array(), 'priv' => array()); foreach ($this->purgeController[$c] as $param => $tags) { if ($param !== 0) { //param1¶m2 $params = explode('&', $param); foreach ($params as $paramName) { if (Tools::getValue($paramName) == false) { continue 2; } } } if (!empty($tags['priv'])) { $conf['priv'] = $tags['priv']; } if (!empty($tags['pub'])) { $conf['pub'] = $tags['pub']; } } if (!empty($conf['priv']) || !empty($conf['pub'])) { return $conf; } return false; } public function getDefaultPurgeTagsByProduct() { // maybe configurable later $tags = array( self::TAG_SEARCH, self::TAG_HOME, self::TAG_SITEMAP, ); return $tags; } public function getDefaultPurgeTagsByCategory() { // maybe configurable later $tags = array( self::TAG_SEARCH, self::TAG_HOME, self::TAG_SITEMAP, ); return $tags; } public function moduleEnabled() { $flag = 0; if (LiteSpeedCacheHelper::licenseEnabled() && $this->get(self::CFG_ENABLED)) { $ips = $this->getArray(self::CFG_ALLOW_IPS); if (!empty($ips)) { // has restricted IP $flag = LiteSpeedCache::CCBM_MOD_ALLOWIP; if (in_array($_SERVER['REMOTE_ADDR'], $ips)) { // allow for restricted IP $flag |= LiteSpeedCache::CCBM_MOD_ACTIVE; } } else { // allow for everyone $flag = LiteSpeedCache::CCBM_MOD_ACTIVE; } } return $flag; } public function getReservedHooks() { $hooks = array( /** global * */ 'actionDispatcher', // check cacheable for route 'displayFooterAfter', // show debug info /** add cache tags * */ 'overrideLayoutTemplate', 'DisplayOverrideTemplate', 'filterCategoryContent', 'filterProductContent', 'filterCmsContent', 'filterCmsCategoryContent', /** private purge * */ 'actionCustomerLogoutAfter', 'actionAuthentication', 'actionCustomerAccountAdd', /** specific price check */ 'actionProductSearchAfter', /** public purge * */ // product 'actionProductAdd', 'actionProductSave', 'actionProductUpdate', //array('id_product' => (int)$this->id, 'product' => $this) 'actionProductDelete', 'actionObjectSpecificPriceCoreAddAfter', 'actionObjectSpecificPriceCoreDeleteAfter', 'actionWatermark', 'displayOrderConfirmation', // from OrderConfirmationController, array('order' => $order) // category 'categoryUpdate', // array('category' => $category) 'actionCategoryUpdate', 'actionCategoryAdd', // here do not purge all, as user can manually do that 'actionCategoryDelete', // cms 'actionObjectCmsUpdateAfter', 'actionObjectCmsDeleteAfter', 'actionObjectCmsAddAfter', // supplier 'actionObjectSupplierUpdateAfter', 'actionObjectSupplierDeleteAfter', 'actionObjectSupplierAddAfter', // manufacturer 'actionObjectManufacturerUpdateAfter', 'actionObjectManufacturerDeleteAfter', 'actionObjectManufacturerAddAfter', // stores 'actionObjectStoreUpdateAfter', /** lscache own hooks * */ 'litespeedCachePurge', 'litespeedNotCacheable', 'litespeedEsiBegin', 'litespeedEsiEnd', // web service 'addWebserviceResources', 'updateProduct', // from Product array('id_product' => ) 'actionUpdateQuantity', // from StockAvailable array('id_product' => $id_product,...) ); return $hooks; } }