├── dev ├── oc2 │ ├── admin │ │ ├── controller │ │ │ └── module │ │ │ │ └── db_cache.php │ │ ├── language │ │ │ ├── english │ │ │ │ └── module │ │ │ │ │ └── db_cache.php │ │ │ └── russian │ │ │ │ └── module │ │ │ │ └── db_cache.php │ │ └── view │ │ │ └── template │ │ │ └── module │ │ │ └── db_cache.tpl │ ├── install.xml │ ├── modification_cleardbcache_menuitem.xml │ ├── readme.txt │ ├── upload │ │ └── system │ │ │ └── library │ │ │ └── pnsols │ │ │ └── db_cache.php │ └── vqmod │ │ └── xml │ │ └── pnsols_dbcache.xml ├── pnsols_dbcache_oc_upload_2016-12-15.tar └── upload │ ├── admin │ ├── controller │ │ └── module │ │ │ └── db_cache.php │ ├── language │ │ ├── english │ │ │ └── module │ │ │ │ └── db_cache.php │ │ └── russian │ │ │ └── module │ │ │ └── db_cache.php │ └── view │ │ └── template │ │ └── module │ │ └── db_cache.tpl │ ├── system │ └── library │ │ └── pnsols │ │ └── db_cache.php │ └── vqmod │ └── xml │ └── pnsols_dbcache.xml ├── dist_oc156x_vqmod ├── readme_oc156x.txt └── upload │ ├── admin │ ├── controller │ │ └── module │ │ │ └── db_cache.php │ ├── language │ │ ├── english │ │ │ └── module │ │ │ │ └── db_cache.php │ │ └── russian │ │ │ └── module │ │ │ └── db_cache.php │ └── view │ │ └── template │ │ └── module │ │ └── db_cache.tpl │ ├── system │ └── library │ │ └── pnsols │ │ └── db_cache.php │ └── vqmod │ └── xml │ └── pnsols_dbcache.xml ├── dist_oc20x_ocmod ├── install.xml ├── readme_oc20x.txt └── upload │ └── system │ └── library │ └── pnsols │ └── db_cache.php ├── readme.txt ├── src └── db_cache.php └── zip ├── pnsols_opencart_db_cache_156x_v1160810.zip ├── pnsols_opencart_db_cache_156x_v1161214.zip └── pnsols_opencart_db_cache_20x.ocmod.zip /dev/oc2/admin/controller/module/db_cache.php: -------------------------------------------------------------------------------- 1 | language->load('module/db_cache'); 9 | 10 | $this->document->setTitle($this->language->get('heading_title')); 11 | 12 | $this->load->model('setting/setting'); 13 | 14 | if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { 15 | $this->model_setting_setting->editSetting('db_cache', $this->request->post); 16 | 17 | $this->session->data['success'] = $this->language->get('text_success'); 18 | 19 | $this->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')); 20 | } 21 | 22 | $this->data['heading_title'] = $this->language->get('heading_title'); 23 | 24 | $this->data['text_enabled'] = $this->language->get('text_enabled'); 25 | $this->data['text_disabled'] = $this->language->get('text_disabled'); 26 | $this->data['text_content_top'] = $this->language->get('text_content_top'); 27 | $this->data['text_content_bottom'] = $this->language->get('text_content_bottom'); 28 | $this->data['text_column_left'] = $this->language->get('text_column_left'); 29 | $this->data['text_column_right'] = $this->language->get('text_column_right'); 30 | 31 | $this->data['entry_cacheTimeoutSeconds'] = $this->language->get('entry_cacheTimeoutSeconds'); 32 | $this->data['entry_status'] = $this->language->get('entry_status'); 33 | $this->data['text_homepage'] = $this->language->get('text_homepage'); 34 | $this->data['text_tab_general'] = $this->language->get('text_tab_general'); 35 | 36 | $this->data['button_save'] = $this->language->get('button_save'); 37 | $this->data['button_cancel'] = $this->language->get('button_cancel'); 38 | $this->data['button_add_module'] = $this->language->get('button_add_module'); 39 | $this->data['button_remove'] = $this->language->get('button_remove'); 40 | 41 | if (isset($this->error['warning'])) { 42 | $this->data['error_warning'] = $this->error['warning']; 43 | } else { 44 | $this->data['error_warning'] = ''; 45 | } 46 | 47 | $this->data['breadcrumbs'] = array(); 48 | 49 | $this->data['breadcrumbs'][] = array( 50 | 'text' => $this->language->get('text_home'), 51 | 'href' => $this->url->link('common/home', 'token=' . $this->session->data['token'], 'SSL'), 52 | 'separator' => false 53 | ); 54 | 55 | $this->data['breadcrumbs'][] = array( 56 | 'text' => $this->language->get('text_module'), 57 | 'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'), 58 | 'separator' => ' :: ' 59 | ); 60 | 61 | $this->data['breadcrumbs'][] = array( 62 | 'text' => $this->language->get('heading_title'), 63 | 'href' => $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'), 64 | 'separator' => ' :: ' 65 | ); 66 | 67 | $this->data['action'] = $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'); 68 | 69 | $this->data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'); 70 | 71 | $this->data['modules'] = array(); 72 | 73 | $this->load->model('design/layout'); 74 | 75 | $this->data['layouts'] = $this->model_design_layout->getLayouts(); 76 | 77 | if (isset($this->request->post['db_cache_status'])) { 78 | $this->data['db_cache_status'] = $this->request->post['db_cache_status']; 79 | } else if ($this->config->get('db_cache_status')) { 80 | $this->data['db_cache_status'] = $this->config->get('db_cache_status'); 81 | } 82 | 83 | if (isset($this->request->post['db_cache_cacheTimeoutSeconds'])) { 84 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->request->post['db_cache_cacheTimeoutSeconds']; 85 | } else if ($this->config->get('db_cache_cacheTimeoutSeconds')) { 86 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->config->get('db_cache_cacheTimeoutSeconds'); 87 | } 88 | 89 | $this->template = 'module/db_cache.tpl'; 90 | $this->children = array( 91 | 'common/header', 92 | 'common/footer' 93 | ); 94 | 95 | $this->response->setOutput($this->render()); 96 | } 97 | 98 | protected function validate() { 99 | if (!$this->user->hasPermission('modify', 'module/db_cache')) { 100 | $this->error['warning'] = $this->language->get('error_permission'); 101 | } 102 | 103 | if (!$this->request->post['db_cache_cacheTimeoutSeconds'] || $this->request->post['db_cache_cacheTimeoutSeconds'] <= 0) { 104 | $this->error['warning'] = $this->language->get('error_cacheTimeoutSeconds'); 105 | } 106 | 107 | if (!$this->error) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | } 113 | 114 | public function install() { 115 | $this->load->model('setting/setting'); 116 | $this->model_setting_setting->editSetting('db_cache', array('db_cache_cacheTimeoutSeconds' => DbCache::DEFAULT_CACHE_TIMEOUT_SECONDS, 'db_cache_status' => 1)); 117 | } 118 | 119 | public function uninstall() { 120 | $this->load->model('setting/setting'); 121 | $this->model_setting_setting->deleteSetting('db_cache'); 122 | $this->getDbCache()->clear(); 123 | } 124 | 125 | public function clear() { 126 | $dbCache = $this->getDbCache(); 127 | $dbCache->clear(); 128 | 129 | $redirectRoute = 'module/db_cache'; 130 | if (isset($this->request->get['redirectRoute'])) { 131 | $redirectRoute = $this->request->get['redirectRoute']; 132 | } 133 | 134 | $this->redirect($this->url->link($redirectRoute, 'token=' . $this->session->data['token'], 'SSL')); 135 | } 136 | 137 | private function getDbCache() { 138 | return DbCache::getInstance(); 139 | } 140 | } 141 | ?> -------------------------------------------------------------------------------- /dev/oc2/admin/language/english/module/db_cache.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 10 | 11 | 12 |
13 | 14 | 15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 52 | 53 |
35 | 36 | $error['cacheTimeoutSeconds'] 37 | 38 |
54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 |

62 |
63 |
64 |
65 | 66 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /dev/oc2/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DB Cache 4 | PNSols_DBCache_Extension_Modification 5 | 1.16.12.14 6 | PN Solutions 7 | http://pnsols.com 8 | 9 | 10 | 11 | 12 | isChanged()) DbCache::getInstance()->saveCacheToFile(); 14 | ]]> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | adaptor->query($sql, $params);]]> 25 | adaptor->query($sql, $params); 32 | ]]> 33 | 34 | 35 | -------------------------------------------------------------------------------- /dev/oc2/modification_cleardbcache_menuitem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ]]> 5 | 7 |
  • Clear DB Cache
  • 8 | 9 | ]]>
    10 |
    11 |
    -------------------------------------------------------------------------------- /dev/oc2/readme.txt: -------------------------------------------------------------------------------- 1 | DB Cache 2 | opencart module 3 | version 1.16.12.14 4 | ——————— 5 | 6 | Caches db sql queries to improve perfomance up to 10 times. 7 | Automatically drop affected cache entries after any modification operation: insert/update/delete 8 | 9 | CHANGES: 10 | v1.16.12.14 11 | - Extension for opencart 2.0+ is released as a modification 12 | - sql query text improvement to adjust mysql query cache 13 | 1.16.08.10: 14 | - Performance is boosted up - db cache kernel is optimized 15 | - Module settings - Cache Timeout Seconds to set for cache entries and Module Status - you could just disable db caching at any moment then turn on again 16 | - Clear DB Cache button is added to Admin System menu 17 | - Small bugs are fixed 18 | 19 | Installation: 20 | ——————- 21 | 2.0+ 22 | 1. Download the pnsols_dbcache_oc2.ocmod.zip file 23 | 2. Go Extensions/Extension Installer 24 | 3. Upload the file and click Next 25 | 4. It is installed and if you need to disable it - go to the Extensions/Modifications and disable/remove the DB Cache 26 | To update to newer version - remove the older and install a new one using the steps above 27 | 28 | PN Solutions http://pnsols.com 2016 29 | info@pnsols.com 30 | -------------------------------------------------------------------------------- /dev/oc2/upload/system/library/pnsols/db_cache.php: -------------------------------------------------------------------------------- 1 | get('config')->get('db_cache_cacheTimeoutSeconds'); 35 | if (!$cacheTimeoutSeconds) { 36 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 37 | } 38 | return $cacheTimeoutSeconds; 39 | */ 40 | return self::DEFAULT_CACHE_TIMEOUT_SECONDS; 41 | } 42 | 43 | private $cacheChanged = FALSE; 44 | 45 | public function isChanged() { 46 | return $this->cacheChanged; 47 | } 48 | 49 | /** 50 | * Protected constructor to prevent creating a new instance of the 51 | * *Singleton* via the `new` operator from outside of this class. 52 | */ 53 | protected function __construct() 54 | { 55 | $this->loadCacheFromFile(); 56 | } 57 | 58 | 59 | 60 | /** 61 | * Private clone method to prevent cloning of the instance of the 62 | * *Singleton* instance. 63 | * 64 | * @return void 65 | */ 66 | private function __clone() 67 | { 68 | } 69 | 70 | /** 71 | * Private unserialize method to prevent unserializing of the *Singleton* 72 | * instance. 73 | * 74 | * @return void 75 | */ 76 | private function __wakeup() 77 | { 78 | } 79 | 80 | private $cacheMap = array(); 81 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 82 | 83 | public function clear() { 84 | $dbCache = $this; 85 | $dirPath = $dbCache->getCacheDirPath(); 86 | $files = scandir($dirPath); 87 | foreach ($files as $filePath) { 88 | if ($filePath == '.' || $filePath == '..') continue; 89 | unlink($dirPath.$filePath); 90 | } 91 | rmdir($dirPath); 92 | } 93 | 94 | public function getCacheDirPath() { 95 | $dirPath = DIR_CACHE.'db_cache/'; 96 | if (!file_exists($dirPath)) mkdir($dirPath); 97 | return $dirPath; 98 | } 99 | 100 | private function getCacheMapFilePath() { 101 | 102 | $dir = DIR_DOWNLOAD . 'db_cache/'; 103 | $dbCacheFilePath = $dir.'db_cache.dat'; 104 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 105 | if (file_exists($dir)) rmdir($dir); 106 | 107 | $latestPath = $this->getCacheDirPath(); 108 | //$latestPath .= '_index'; 109 | $latestPath .= '_cache'; 110 | 111 | return $latestPath; 112 | } 113 | 114 | public function loadCacheFromFile() { 115 | $cacheFilePath = $this->getCacheMapFilePath(); 116 | if (!file_exists($cacheFilePath)) 117 | return; 118 | $handle = fopen($cacheFilePath, "r"); 119 | flock($handle, LOCK_SH); 120 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 121 | fclose($handle); 122 | $this->cacheMap = unserialize($cacheSerialized); 123 | } 124 | 125 | public function saveCacheToFile() { 126 | $cacheFilePath = $this->getCacheMapFilePath(); 127 | 128 | $cacheSerialized = serialize($this->cacheMap); 129 | 130 | $handle = fopen($cacheFilePath, 'w'); 131 | flock($handle, LOCK_EX); 132 | fwrite($handle, $cacheSerialized); 133 | fflush($handle); 134 | fclose($handle); 135 | } 136 | 137 | public function addSelectFetchToCache($queryText, $fetchData) { 138 | $this->setCacheEntry($queryText, $fetchData); 139 | } 140 | 141 | public function getCachedSelectFetch($queryText) { 142 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 143 | //$cachedFetchData = $this->getCacheEntryData($queryText); 144 | 145 | return $cachedFetchData; 146 | } 147 | 148 | private function getCachedDataFromCacheMap($queryText) { 149 | 150 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 151 | $cachedEntry = $this->cacheMap[$queryText]; 152 | $cacheTime = $cachedEntry['time']; 153 | $nowTime = date_create(); 154 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 155 | $daysDiffCount = $secondsDiffSpan->format('%a'); 156 | if ($daysDiffCount >= self::getCacheTimeout()) { 157 | $this->removeCacheEntry($queryText); 158 | return null; 159 | } 160 | return $cachedEntry['data']; 161 | } 162 | return null; 163 | } 164 | 165 | public function processModificationQuery($queryText) { 166 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 167 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 168 | foreach ($dbTableNamesInQuery as $dbTableName) { 169 | if (stripos($queryTextKey, $dbTableName)) { 170 | $this->removeCacheEntry($queryTextKey); 171 | //echo 'remove db cache: '.$queryTextKey.'
    '; 172 | } 173 | } 174 | } 175 | } 176 | 177 | private function getCacheEntryFilePath($cacheKey) { 178 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 179 | } 180 | 181 | private function getCacheEntryFileNameByHash($hash) { 182 | return $hash; 183 | } 184 | 185 | private function getCacheEntryData($cacheKey) { 186 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 187 | $nowTime = date_create(); 188 | 189 | $timeModified = date_create(); 190 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 191 | 192 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 193 | $daysDiffCount = $secondsDiffSpan->format('%a'); 194 | if ($daysDiffCount >= self::getCacheTimeout()) { 195 | $this->removeCacheEntry($cacheKey); 196 | return NULL; 197 | } 198 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 199 | 200 | } 201 | 202 | private function removeCacheEntry($queryTextKey) { 203 | if (isset($this->cacheMap[$queryTextKey])) { 204 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 205 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 206 | $this->cacheMap[$queryTextKey] = null; 207 | unset($this->cacheMap[$queryTextKey]); 208 | $this->cacheChanged = TRUE; 209 | } 210 | } 211 | 212 | private function getSelectQueryHash($queryText) { 213 | return md5($queryText); 214 | } 215 | 216 | private function setCacheEntry($cacheKey, $cacheData) { 217 | $cachedTime = date_create(); 218 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 219 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 220 | $this->cacheChanged = TRUE; 221 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 222 | } 223 | 224 | public static function isModificationQuery($queryText) { 225 | //strpos(trim(strtolower($queryText)), "select"); 226 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 227 | //$isModQuery = $startsWithSelect === FALSE; 228 | 229 | //return $isModQuery; 230 | $arReadQueries = array('select', 'show tables', 'show columns'); 231 | foreach ($arReadQueries as $queryRead) { 232 | $striposSelect = stripos(trim($queryText), $queryRead); 233 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 234 | } 235 | return TRUE; 236 | } 237 | 238 | public static function extractDbTableNamesFromQueryText($queryText) { 239 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 240 | return $tableNames; 241 | } 242 | 243 | public static function processDbQuery($db, $sql, $params = NULL) { 244 | if ($params === NULL) $params = array(); 245 | $dbCache = DbCache::getInstance(); 246 | if (stripos($sql, 'now()')) { 247 | $c = 0; 248 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 249 | } 250 | if (DbCache::isModificationQuery($sql)) { 251 | $dbCache->processModificationQuery($sql); 252 | } else { 253 | //if (!Registry::getInstance()->get('config')->get('db_cache_status')) 254 | //return $db->queryNonCache($sql); 255 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 256 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 257 | if ($cachedFetch != null) { 258 | //echo 'cached select query: '.$sql; 259 | return $cachedFetch; 260 | } 261 | } else { 262 | return $db->queryNonCache($sql); 263 | } 264 | } 265 | 266 | $freshDbFetch = $db->queryNonCache($sql); 267 | if (!DbCache::isModificationQuery($sql)) { 268 | //echo 'add cache select query: '.$sql; 269 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 270 | } 271 | return $freshDbFetch; 272 | } 273 | 274 | } 275 | 276 | 277 | 278 | ?> -------------------------------------------------------------------------------- /dev/oc2/vqmod/xml/pnsols_dbcache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DB Cache for OC 4 | 1.5.x and above 5 | 2.3.0 6 | PN Solutions 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | get('config')->get('db_cache_status')) { 20 | if (DbCache::isCreated() && DbCache::getInstance()->isChanged()) DbCache::getInstance()->saveCacheToFile(); 21 | //} 22 | ]]> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 42 | 43 | 44 | 45 | driver->query($sql);]]> 46 | driver->query($sql); 53 | ]]> 54 | 55 | 56 | 57 | 58 | 59 | ]]> 60 | Clear DB Cache]]> 61 | 62 | 63 | -------------------------------------------------------------------------------- /dev/pnsols_dbcache_oc_upload_2016-12-15.tar: -------------------------------------------------------------------------------- 1 | admin/controller/module/db_cache.php0000664000000000007740000001237212752712100017366 0ustar rootwebadminlanguage->load('module/db_cache'); 9 | 10 | $this->document->setTitle($this->language->get('heading_title')); 11 | 12 | $this->load->model('setting/setting'); 13 | 14 | if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { 15 | $this->model_setting_setting->editSetting('db_cache', $this->request->post); 16 | 17 | $this->session->data['success'] = $this->language->get('text_success'); 18 | 19 | $this->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')); 20 | } 21 | 22 | $this->data['heading_title'] = $this->language->get('heading_title'); 23 | 24 | $this->data['text_enabled'] = $this->language->get('text_enabled'); 25 | $this->data['text_disabled'] = $this->language->get('text_disabled'); 26 | $this->data['text_content_top'] = $this->language->get('text_content_top'); 27 | $this->data['text_content_bottom'] = $this->language->get('text_content_bottom'); 28 | $this->data['text_column_left'] = $this->language->get('text_column_left'); 29 | $this->data['text_column_right'] = $this->language->get('text_column_right'); 30 | 31 | $this->data['entry_cacheTimeoutSeconds'] = $this->language->get('entry_cacheTimeoutSeconds'); 32 | $this->data['entry_status'] = $this->language->get('entry_status'); 33 | $this->data['text_homepage'] = $this->language->get('text_homepage'); 34 | $this->data['text_tab_general'] = $this->language->get('text_tab_general'); 35 | 36 | $this->data['button_save'] = $this->language->get('button_save'); 37 | $this->data['button_cancel'] = $this->language->get('button_cancel'); 38 | $this->data['button_add_module'] = $this->language->get('button_add_module'); 39 | $this->data['button_remove'] = $this->language->get('button_remove'); 40 | 41 | if (isset($this->error['warning'])) { 42 | $this->data['error_warning'] = $this->error['warning']; 43 | } else { 44 | $this->data['error_warning'] = ''; 45 | } 46 | 47 | $this->data['breadcrumbs'] = array(); 48 | 49 | $this->data['breadcrumbs'][] = array( 50 | 'text' => $this->language->get('text_home'), 51 | 'href' => $this->url->link('common/home', 'token=' . $this->session->data['token'], 'SSL'), 52 | 'separator' => false 53 | ); 54 | 55 | $this->data['breadcrumbs'][] = array( 56 | 'text' => $this->language->get('text_module'), 57 | 'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'), 58 | 'separator' => ' :: ' 59 | ); 60 | 61 | $this->data['breadcrumbs'][] = array( 62 | 'text' => $this->language->get('heading_title'), 63 | 'href' => $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'), 64 | 'separator' => ' :: ' 65 | ); 66 | 67 | $this->data['action'] = $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'); 68 | 69 | $this->data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'); 70 | 71 | $this->data['modules'] = array(); 72 | 73 | $this->load->model('design/layout'); 74 | 75 | $this->data['layouts'] = $this->model_design_layout->getLayouts(); 76 | 77 | if (isset($this->request->post['db_cache_status'])) { 78 | $this->data['db_cache_status'] = $this->request->post['db_cache_status']; 79 | } else if ($this->config->get('db_cache_status')) { 80 | $this->data['db_cache_status'] = $this->config->get('db_cache_status'); 81 | } 82 | 83 | if (isset($this->request->post['db_cache_cacheTimeoutSeconds'])) { 84 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->request->post['db_cache_cacheTimeoutSeconds']; 85 | } else if ($this->config->get('db_cache_cacheTimeoutSeconds')) { 86 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->config->get('db_cache_cacheTimeoutSeconds'); 87 | } 88 | 89 | $this->template = 'module/db_cache.tpl'; 90 | $this->children = array( 91 | 'common/header', 92 | 'common/footer' 93 | ); 94 | 95 | $this->response->setOutput($this->render()); 96 | } 97 | 98 | protected function validate() { 99 | if (!$this->user->hasPermission('modify', 'module/db_cache')) { 100 | $this->error['warning'] = $this->language->get('error_permission'); 101 | } 102 | 103 | if (!$this->request->post['db_cache_cacheTimeoutSeconds'] || $this->request->post['db_cache_cacheTimeoutSeconds'] <= 0) { 104 | $this->error['warning'] = $this->language->get('error_cacheTimeoutSeconds'); 105 | } 106 | 107 | if (!$this->error) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | } 113 | 114 | public function install() { 115 | $this->load->model('setting/setting'); 116 | $this->model_setting_setting->editSetting('db_cache', array('db_cache_cacheTimeoutSeconds' => DbCache::DEFAULT_CACHE_TIMEOUT_SECONDS, 'db_cache_status' => 1)); 117 | } 118 | 119 | public function uninstall() { 120 | $this->load->model('setting/setting'); 121 | $this->model_setting_setting->deleteSetting('db_cache'); 122 | $this->getDbCache()->clear(); 123 | } 124 | 125 | public function clear() { 126 | $dbCache = $this->getDbCache(); 127 | $dbCache->clear(); 128 | 129 | $redirectRoute = 'module/db_cache'; 130 | if (isset($this->request->get['redirectRoute'])) { 131 | $redirectRoute = $this->request->get['redirectRoute']; 132 | } 133 | 134 | $this->redirect($this->url->link($redirectRoute, 'token=' . $this->session->data['token'], 'SSL')); 135 | } 136 | 137 | private function getDbCache() { 138 | return DbCache::getInstance(); 139 | } 140 | } 141 | ?>admin/language/english/module/db_cache.php0000664000000000007740000000071212752627212020423 0ustar rootwebadmin 173 | 174 |
    175 | 176 | 181 | 182 | 183 |
    184 | 185 | 186 |
    187 |
    188 |
    189 |
    190 |

    191 |
    192 |
    193 |
    194 |
    195 | 196 |
    197 | 198 | 199 |
    200 | 201 |
    202 | 203 | 204 | 205 | 210 | 211 | 212 | 213 | 214 | 223 | 224 |
    206 | 207 | $error['cacheTimeoutSeconds'] 208 | 209 |
    225 |
    226 | 227 |
    228 | 229 |
    230 | 231 |
    232 |

    233 |
    234 |
    235 |
    236 | 237 | 240 | 241 | 242 | 243 | vqmod/xml/pnsols_dbcache.xml0000664000000000007740000000420113022115617016015 0ustar rootwebadmin 244 | 245 | DB Cache for OC 246 | 1.5.x and above 247 | 2.3.0 248 | PN Solutions 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | get('config')->get('db_cache_status')) { 262 | if (DbCache::isCreated() && DbCache::getInstance()->isChanged()) DbCache::getInstance()->saveCacheToFile(); 263 | //} 264 | ]]> 265 | 266 | 267 | 268 | 269 | 270 | 271 | 282 | 283 | 284 | 285 | 286 | 287 | driver->query($sql);]]> 288 | driver->query($sql); 295 | ]]> 296 | 297 | 298 | 299 | 300 | 301 | ]]> 302 | Clear DB Cache]]> 303 | 304 | 305 | system/library/pnsols/db_cache.php0000664000000000007740000002130013024263735017133 0ustar rootwebadminget('config')->get('db_cache_cacheTimeoutSeconds'); 338 | if (!$cacheTimeoutSeconds) { 339 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 340 | } 341 | return $cacheTimeoutSeconds 342 | } 343 | 344 | private $cacheChanged = FALSE; 345 | 346 | public function isChanged() { 347 | return $this->cacheChanged; 348 | } 349 | 350 | /** 351 | * Protected constructor to prevent creating a new instance of the 352 | * *Singleton* via the `new` operator from outside of this class. 353 | */ 354 | protected function __construct() 355 | { 356 | $this->loadCacheFromFile(); 357 | } 358 | 359 | 360 | 361 | /** 362 | * Private clone method to prevent cloning of the instance of the 363 | * *Singleton* instance. 364 | * 365 | * @return void 366 | */ 367 | private function __clone() 368 | { 369 | } 370 | 371 | /** 372 | * Private unserialize method to prevent unserializing of the *Singleton* 373 | * instance. 374 | * 375 | * @return void 376 | */ 377 | private function __wakeup() 378 | { 379 | } 380 | 381 | private $cacheMap = array(); 382 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 383 | 384 | public function clear() { 385 | $dbCache = $this; 386 | $dirPath = $dbCache->getCacheDirPath(); 387 | $files = scandir($dirPath); 388 | foreach ($files as $filePath) { 389 | if ($filePath == '.' || $filePath == '..') continue; 390 | unlink($dirPath.$filePath); 391 | } 392 | rmdir($dirPath); 393 | } 394 | 395 | public function getCacheDirPath() { 396 | $dirPath = DIR_CACHE.'db_cache/'; 397 | if (!file_exists($dirPath)) mkdir($dirPath); 398 | return $dirPath; 399 | } 400 | 401 | private function getCacheMapFilePath() { 402 | 403 | $dir = DIR_DOWNLOAD . 'db_cache/'; 404 | $dbCacheFilePath = $dir.'db_cache.dat'; 405 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 406 | if (file_exists($dir)) rmdir($dir); 407 | 408 | $latestPath = $this->getCacheDirPath(); 409 | //$latestPath .= '_index'; 410 | $latestPath .= '_cache'; 411 | 412 | return $latestPath; 413 | } 414 | 415 | public function loadCacheFromFile() { 416 | $cacheFilePath = $this->getCacheMapFilePath(); 417 | if (!file_exists($cacheFilePath)) 418 | return; 419 | $handle = fopen($cacheFilePath, "r"); 420 | flock($handle, LOCK_SH); 421 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 422 | fclose($handle); 423 | $this->cacheMap = unserialize($cacheSerialized); 424 | } 425 | 426 | public function saveCacheToFile() { 427 | $cacheFilePath = $this->getCacheMapFilePath(); 428 | 429 | $cacheSerialized = serialize($this->cacheMap); 430 | 431 | $handle = fopen($cacheFilePath, 'w'); 432 | flock($handle, LOCK_EX); 433 | fwrite($handle, $cacheSerialized); 434 | fflush($handle); 435 | fclose($handle); 436 | } 437 | 438 | public function addSelectFetchToCache($queryText, $fetchData) { 439 | $this->setCacheEntry($queryText, $fetchData); 440 | } 441 | 442 | public function getCachedSelectFetch($queryText) { 443 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 444 | //$cachedFetchData = $this->getCacheEntryData($queryText); 445 | 446 | return $cachedFetchData; 447 | } 448 | 449 | private function getCachedDataFromCacheMap($queryText) { 450 | 451 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 452 | $cachedEntry = $this->cacheMap[$queryText]; 453 | $cacheTime = $cachedEntry['time']; 454 | $nowTime = date_create(); 455 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 456 | $daysDiffCount = $secondsDiffSpan->format('%a'); 457 | if ($daysDiffCount >= self::getCacheTimeout()) { 458 | $this->removeCacheEntry($queryText); 459 | return null; 460 | } 461 | return $cachedEntry['data']; 462 | } 463 | return null; 464 | } 465 | 466 | public function processModificationQuery($queryText) { 467 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 468 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 469 | foreach ($dbTableNamesInQuery as $dbTableName) { 470 | if (stripos($queryTextKey, $dbTableName)) { 471 | $this->removeCacheEntry($queryTextKey); 472 | //echo 'remove db cache: '.$queryTextKey.'
    '; 473 | } 474 | } 475 | } 476 | } 477 | 478 | private function getCacheEntryFilePath($cacheKey) { 479 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 480 | } 481 | 482 | private function getCacheEntryFileNameByHash($hash) { 483 | return $hash; 484 | } 485 | 486 | private function getCacheEntryData($cacheKey) { 487 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 488 | $nowTime = date_create(); 489 | 490 | $timeModified = date_create(); 491 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 492 | 493 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 494 | $daysDiffCount = $secondsDiffSpan->format('%a'); 495 | if ($daysDiffCount >= self::getCacheTimeout()) { 496 | $this->removeCacheEntry($cacheKey); 497 | return NULL; 498 | } 499 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 500 | 501 | } 502 | 503 | private function removeCacheEntry($queryTextKey) { 504 | if (isset($this->cacheMap[$queryTextKey])) { 505 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 506 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 507 | $this->cacheMap[$queryTextKey] = null; 508 | unset($this->cacheMap[$queryTextKey]); 509 | $this->cacheChanged = TRUE; 510 | } 511 | } 512 | 513 | private function getSelectQueryHash($queryText) { 514 | return md5($queryText); 515 | } 516 | 517 | private function setCacheEntry($cacheKey, $cacheData) { 518 | $cachedTime = date_create(); 519 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 520 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 521 | $this->cacheChanged = TRUE; 522 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 523 | } 524 | 525 | public static function isModificationQuery($queryText) { 526 | //strpos(trim(strtolower($queryText)), "select"); 527 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 528 | //$isModQuery = $startsWithSelect === FALSE; 529 | 530 | //return $isModQuery; 531 | $arReadQueries = array('select', 'show tables', 'show columns'); 532 | foreach ($arReadQueries as $queryRead) { 533 | $striposSelect = stripos(trim($queryText), $queryRead); 534 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 535 | } 536 | return TRUE; 537 | } 538 | 539 | public static function extractDbTableNamesFromQueryText($queryText) { 540 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 541 | return $tableNames; 542 | } 543 | 544 | public static function processDbQuery($db, $sql) { 545 | $dbCache = DbCache::getInstance(); 546 | if (stripos($sql, 'now()')) { 547 | $c = 0; 548 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 549 | } 550 | if (DbCache::isModificationQuery($sql)) { 551 | $dbCache->processModificationQuery($sql); 552 | } else { 553 | if (!Registry::getInstance()->get('config')->get('db_cache_status')) 554 | return $db->queryNonCache($sql); 555 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 556 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 557 | if ($cachedFetch != null) { 558 | //echo 'cached select query: '.$sql; 559 | return $cachedFetch; 560 | } 561 | } else { 562 | return $db->queryNonCache($sql); 563 | } 564 | } 565 | 566 | $freshDbFetch = $db->queryNonCache($sql); 567 | if (!DbCache::isModificationQuery($sql)) { 568 | //echo 'add cache select query: '.$sql; 569 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 570 | } 571 | return $freshDbFetch; 572 | } 573 | 574 | } 575 | 576 | 577 | 578 | ?> -------------------------------------------------------------------------------- /dev/upload/admin/controller/module/db_cache.php: -------------------------------------------------------------------------------- 1 | language->load('module/db_cache'); 9 | 10 | $this->document->setTitle($this->language->get('heading_title')); 11 | 12 | $this->load->model('setting/setting'); 13 | 14 | if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { 15 | $this->model_setting_setting->editSetting('db_cache', $this->request->post); 16 | 17 | $this->session->data['success'] = $this->language->get('text_success'); 18 | 19 | $this->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')); 20 | } 21 | 22 | $this->data['heading_title'] = $this->language->get('heading_title'); 23 | 24 | $this->data['text_enabled'] = $this->language->get('text_enabled'); 25 | $this->data['text_disabled'] = $this->language->get('text_disabled'); 26 | $this->data['text_content_top'] = $this->language->get('text_content_top'); 27 | $this->data['text_content_bottom'] = $this->language->get('text_content_bottom'); 28 | $this->data['text_column_left'] = $this->language->get('text_column_left'); 29 | $this->data['text_column_right'] = $this->language->get('text_column_right'); 30 | 31 | $this->data['entry_cacheTimeoutSeconds'] = $this->language->get('entry_cacheTimeoutSeconds'); 32 | $this->data['entry_status'] = $this->language->get('entry_status'); 33 | $this->data['text_homepage'] = $this->language->get('text_homepage'); 34 | $this->data['text_tab_general'] = $this->language->get('text_tab_general'); 35 | 36 | $this->data['button_save'] = $this->language->get('button_save'); 37 | $this->data['button_cancel'] = $this->language->get('button_cancel'); 38 | $this->data['button_add_module'] = $this->language->get('button_add_module'); 39 | $this->data['button_remove'] = $this->language->get('button_remove'); 40 | 41 | if (isset($this->error['warning'])) { 42 | $this->data['error_warning'] = $this->error['warning']; 43 | } else { 44 | $this->data['error_warning'] = ''; 45 | } 46 | 47 | $this->data['breadcrumbs'] = array(); 48 | 49 | $this->data['breadcrumbs'][] = array( 50 | 'text' => $this->language->get('text_home'), 51 | 'href' => $this->url->link('common/home', 'token=' . $this->session->data['token'], 'SSL'), 52 | 'separator' => false 53 | ); 54 | 55 | $this->data['breadcrumbs'][] = array( 56 | 'text' => $this->language->get('text_module'), 57 | 'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'), 58 | 'separator' => ' :: ' 59 | ); 60 | 61 | $this->data['breadcrumbs'][] = array( 62 | 'text' => $this->language->get('heading_title'), 63 | 'href' => $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'), 64 | 'separator' => ' :: ' 65 | ); 66 | 67 | $this->data['action'] = $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'); 68 | 69 | $this->data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'); 70 | 71 | $this->data['modules'] = array(); 72 | 73 | $this->load->model('design/layout'); 74 | 75 | $this->data['layouts'] = $this->model_design_layout->getLayouts(); 76 | 77 | if (isset($this->request->post['db_cache_status'])) { 78 | $this->data['db_cache_status'] = $this->request->post['db_cache_status']; 79 | } else if ($this->config->get('db_cache_status')) { 80 | $this->data['db_cache_status'] = $this->config->get('db_cache_status'); 81 | } 82 | 83 | if (isset($this->request->post['db_cache_cacheTimeoutSeconds'])) { 84 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->request->post['db_cache_cacheTimeoutSeconds']; 85 | } else if ($this->config->get('db_cache_cacheTimeoutSeconds')) { 86 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->config->get('db_cache_cacheTimeoutSeconds'); 87 | } 88 | 89 | $this->template = 'module/db_cache.tpl'; 90 | $this->children = array( 91 | 'common/header', 92 | 'common/footer' 93 | ); 94 | 95 | $this->response->setOutput($this->render()); 96 | } 97 | 98 | protected function validate() { 99 | if (!$this->user->hasPermission('modify', 'module/db_cache')) { 100 | $this->error['warning'] = $this->language->get('error_permission'); 101 | } 102 | 103 | if (!$this->request->post['db_cache_cacheTimeoutSeconds'] || $this->request->post['db_cache_cacheTimeoutSeconds'] <= 0) { 104 | $this->error['warning'] = $this->language->get('error_cacheTimeoutSeconds'); 105 | } 106 | 107 | if (!$this->error) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | } 113 | 114 | public function install() { 115 | $this->load->model('setting/setting'); 116 | $this->model_setting_setting->editSetting('db_cache', array('db_cache_cacheTimeoutSeconds' => DbCache::DEFAULT_CACHE_TIMEOUT_SECONDS, 'db_cache_status' => 1)); 117 | } 118 | 119 | public function uninstall() { 120 | $this->load->model('setting/setting'); 121 | $this->model_setting_setting->deleteSetting('db_cache'); 122 | $this->getDbCache()->clear(); 123 | } 124 | 125 | public function clear() { 126 | $dbCache = $this->getDbCache(); 127 | $dbCache->clear(); 128 | 129 | $redirectRoute = 'module/db_cache'; 130 | if (isset($this->request->get['redirectRoute'])) { 131 | $redirectRoute = $this->request->get['redirectRoute']; 132 | } 133 | 134 | $this->redirect($this->url->link($redirectRoute, 'token=' . $this->session->data['token'], 'SSL')); 135 | } 136 | 137 | private function getDbCache() { 138 | return DbCache::getInstance(); 139 | } 140 | } 141 | ?> -------------------------------------------------------------------------------- /dev/upload/admin/language/english/module/db_cache.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | 5 | 10 | 11 | 12 |
    13 | 14 | 15 |
    16 |
    17 |
    18 |
    19 |

    20 |
    21 |
    22 |
    23 |
    24 | 25 |
    26 | 27 | 28 |
    29 | 30 |
    31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 52 | 53 |
    35 | 36 | $error['cacheTimeoutSeconds'] 37 | 38 |
    54 |
    55 | 56 |
    57 | 58 |
    59 | 60 |
    61 |

    62 |
    63 |
    64 |
    65 | 66 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /dev/upload/system/library/pnsols/db_cache.php: -------------------------------------------------------------------------------- 1 | get('config')->get('db_cache_cacheTimeoutSeconds'); 34 | if (!$cacheTimeoutSeconds) { 35 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 36 | } 37 | return $cacheTimeoutSeconds; 38 | } 39 | 40 | private $cacheChanged = FALSE; 41 | 42 | public function isChanged() { 43 | return $this->cacheChanged; 44 | } 45 | 46 | /** 47 | * Protected constructor to prevent creating a new instance of the 48 | * *Singleton* via the `new` operator from outside of this class. 49 | */ 50 | protected function __construct() 51 | { 52 | $this->loadCacheFromFile(); 53 | } 54 | 55 | 56 | 57 | /** 58 | * Private clone method to prevent cloning of the instance of the 59 | * *Singleton* instance. 60 | * 61 | * @return void 62 | */ 63 | private function __clone() 64 | { 65 | } 66 | 67 | /** 68 | * Private unserialize method to prevent unserializing of the *Singleton* 69 | * instance. 70 | * 71 | * @return void 72 | */ 73 | private function __wakeup() 74 | { 75 | } 76 | 77 | private $cacheMap = array(); 78 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 79 | 80 | public function clear() { 81 | $dbCache = $this; 82 | $dirPath = $dbCache->getCacheDirPath(); 83 | $files = scandir($dirPath); 84 | foreach ($files as $filePath) { 85 | if ($filePath == '.' || $filePath == '..') continue; 86 | unlink($dirPath.$filePath); 87 | } 88 | rmdir($dirPath); 89 | } 90 | 91 | public function getCacheDirPath() { 92 | $dirPath = DIR_CACHE.'db_cache/'; 93 | if (!file_exists($dirPath)) mkdir($dirPath); 94 | return $dirPath; 95 | } 96 | 97 | private function getCacheMapFilePath() { 98 | 99 | $dir = DIR_DOWNLOAD . 'db_cache/'; 100 | $dbCacheFilePath = $dir.'db_cache.dat'; 101 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 102 | if (file_exists($dir)) rmdir($dir); 103 | 104 | $latestPath = $this->getCacheDirPath(); 105 | //$latestPath .= '_index'; 106 | $latestPath .= '_cache'; 107 | 108 | return $latestPath; 109 | } 110 | 111 | public function loadCacheFromFile() { 112 | $cacheFilePath = $this->getCacheMapFilePath(); 113 | if (!file_exists($cacheFilePath)) 114 | return; 115 | $handle = fopen($cacheFilePath, "r"); 116 | flock($handle, LOCK_SH); 117 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 118 | fclose($handle); 119 | $this->cacheMap = unserialize($cacheSerialized); 120 | } 121 | 122 | public function saveCacheToFile() { 123 | $cacheFilePath = $this->getCacheMapFilePath(); 124 | 125 | $cacheSerialized = serialize($this->cacheMap); 126 | 127 | $handle = fopen($cacheFilePath, 'w'); 128 | flock($handle, LOCK_EX); 129 | fwrite($handle, $cacheSerialized); 130 | fflush($handle); 131 | fclose($handle); 132 | } 133 | 134 | public function addSelectFetchToCache($queryText, $fetchData) { 135 | $this->setCacheEntry($queryText, $fetchData); 136 | } 137 | 138 | public function getCachedSelectFetch($queryText) { 139 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 140 | //$cachedFetchData = $this->getCacheEntryData($queryText); 141 | 142 | return $cachedFetchData; 143 | } 144 | 145 | private function getCachedDataFromCacheMap($queryText) { 146 | 147 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 148 | $cachedEntry = $this->cacheMap[$queryText]; 149 | $cacheTime = $cachedEntry['time']; 150 | $nowTime = date_create(); 151 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 152 | $daysDiffCount = $secondsDiffSpan->format('%a'); 153 | if ($daysDiffCount >= self::getCacheTimeout()) { 154 | $this->removeCacheEntry($queryText); 155 | return null; 156 | } 157 | return $cachedEntry['data']; 158 | } 159 | return null; 160 | } 161 | 162 | public function processModificationQuery($queryText) { 163 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 164 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 165 | foreach ($dbTableNamesInQuery as $dbTableName) { 166 | if (stripos($queryTextKey, $dbTableName)) { 167 | $this->removeCacheEntry($queryTextKey); 168 | //echo 'remove db cache: '.$queryTextKey.'
    '; 169 | } 170 | } 171 | } 172 | } 173 | 174 | private function getCacheEntryFilePath($cacheKey) { 175 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 176 | } 177 | 178 | private function getCacheEntryFileNameByHash($hash) { 179 | return $hash; 180 | } 181 | 182 | private function getCacheEntryData($cacheKey) { 183 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 184 | $nowTime = date_create(); 185 | 186 | $timeModified = date_create(); 187 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 188 | 189 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 190 | $daysDiffCount = $secondsDiffSpan->format('%a'); 191 | if ($daysDiffCount >= self::getCacheTimeout()) { 192 | $this->removeCacheEntry($cacheKey); 193 | return NULL; 194 | } 195 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 196 | 197 | } 198 | 199 | private function removeCacheEntry($queryTextKey) { 200 | if (isset($this->cacheMap[$queryTextKey])) { 201 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 202 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 203 | $this->cacheMap[$queryTextKey] = null; 204 | unset($this->cacheMap[$queryTextKey]); 205 | $this->cacheChanged = TRUE; 206 | } 207 | } 208 | 209 | private function getSelectQueryHash($queryText) { 210 | return md5($queryText); 211 | } 212 | 213 | private function setCacheEntry($cacheKey, $cacheData) { 214 | $cachedTime = date_create(); 215 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 216 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 217 | $this->cacheChanged = TRUE; 218 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 219 | } 220 | 221 | public static function isModificationQuery($queryText) { 222 | //strpos(trim(strtolower($queryText)), "select"); 223 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 224 | //$isModQuery = $startsWithSelect === FALSE; 225 | 226 | //return $isModQuery; 227 | $arReadQueries = array('select', 'show tables', 'show columns'); 228 | foreach ($arReadQueries as $queryRead) { 229 | $striposSelect = stripos(trim($queryText), $queryRead); 230 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 231 | } 232 | return TRUE; 233 | } 234 | 235 | public static function extractDbTableNamesFromQueryText($queryText) { 236 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 237 | return $tableNames; 238 | } 239 | 240 | public static function processDbQuery($db, $sql) { 241 | $dbCache = DbCache::getInstance(); 242 | if (stripos($sql, 'now()')) { 243 | $c = 0; 244 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 245 | } 246 | if (DbCache::isModificationQuery($sql)) { 247 | $dbCache->processModificationQuery($sql); 248 | } else { 249 | if (!Registry::getInstance()->get('config')->get('db_cache_status')) 250 | return $db->queryNonCache($sql); 251 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 252 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 253 | if ($cachedFetch != null) { 254 | //echo 'cached select query: '.$sql; 255 | return $cachedFetch; 256 | } 257 | } else { 258 | return $db->queryNonCache($sql); 259 | } 260 | } 261 | 262 | $freshDbFetch = $db->queryNonCache($sql); 263 | if (!DbCache::isModificationQuery($sql)) { 264 | //echo 'add cache select query: '.$sql; 265 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 266 | } 267 | return $freshDbFetch; 268 | } 269 | 270 | } 271 | 272 | 273 | 274 | ?> -------------------------------------------------------------------------------- /dev/upload/vqmod/xml/pnsols_dbcache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DB Cache for OC 4 | 1.16.12.14 5 | 2.3.0 6 | PN Solutions 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | get('config')->get('db_cache_status')) { 20 | if (DbCache::isCreated() && DbCache::getInstance()->isChanged()) DbCache::getInstance()->saveCacheToFile(); 21 | } 22 | ]]> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 42 | 43 | 44 | 45 | driver->query($sql);]]> 46 | get('config')->get('db_cache_status')) 48 | return $this->queryNonCache($sql); 49 | 50 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 51 | return DbCache::processDbQuery($this, $sql); 52 | } 53 | return $this->queryNonCache($sql); 54 | } 55 | 56 | public function queryNonCache($sql) { 57 | return $this->driver->query($sql); 58 | ]]> 59 | 60 | 61 | 62 | 63 | 64 | ]]> 65 | Clear DB Cache]]> 66 | 67 | 68 | -------------------------------------------------------------------------------- /dist_oc156x_vqmod/readme_oc156x.txt: -------------------------------------------------------------------------------- 1 | DB Cache 2 | opencart module 3 | version 1.16.12.14 4 | ——————— 5 | 6 | VQMOD required 7 | 8 | Caches db sql queries to improve perfomance up to 10 times. 9 | Automatically drop affected cache entries after any modification operation: insert/update/delete 10 | 11 | CHANGES: 12 | 1.16.12.14: 13 | - sql query text is adjusted for mysql query cache 14 | 1.16.08.10: 15 | - Performance is boosted up - db cache kernel is optimized 16 | - Module settings - Cache Timeout Seconds to set for cache entries and Module Status - you could just disable db caching at any moment then turn on again 17 | - Clear DB Cache button is added to Admin System menu 18 | - Small bugs are fixed 19 | 20 | Installation: 21 | ——————- 22 | 1. Copy upload folder content to the root of your site. 23 | 2. Install DB Cache module 24 | 3. Enjoy perfomance boost 25 | 26 | tested on opencart 1.5.6.3 27 | 28 | PN Solutions http://pnsols.com 2016 29 | info@pnsols.com 30 | -------------------------------------------------------------------------------- /dist_oc156x_vqmod/upload/admin/controller/module/db_cache.php: -------------------------------------------------------------------------------- 1 | language->load('module/db_cache'); 9 | 10 | $this->document->setTitle($this->language->get('heading_title')); 11 | 12 | $this->load->model('setting/setting'); 13 | 14 | if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { 15 | $this->model_setting_setting->editSetting('db_cache', $this->request->post); 16 | 17 | $this->session->data['success'] = $this->language->get('text_success'); 18 | 19 | $this->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')); 20 | } 21 | 22 | $this->data['heading_title'] = $this->language->get('heading_title'); 23 | 24 | $this->data['text_enabled'] = $this->language->get('text_enabled'); 25 | $this->data['text_disabled'] = $this->language->get('text_disabled'); 26 | $this->data['text_content_top'] = $this->language->get('text_content_top'); 27 | $this->data['text_content_bottom'] = $this->language->get('text_content_bottom'); 28 | $this->data['text_column_left'] = $this->language->get('text_column_left'); 29 | $this->data['text_column_right'] = $this->language->get('text_column_right'); 30 | 31 | $this->data['entry_cacheTimeoutSeconds'] = $this->language->get('entry_cacheTimeoutSeconds'); 32 | $this->data['entry_status'] = $this->language->get('entry_status'); 33 | $this->data['text_homepage'] = $this->language->get('text_homepage'); 34 | $this->data['text_tab_general'] = $this->language->get('text_tab_general'); 35 | 36 | $this->data['button_save'] = $this->language->get('button_save'); 37 | $this->data['button_cancel'] = $this->language->get('button_cancel'); 38 | $this->data['button_add_module'] = $this->language->get('button_add_module'); 39 | $this->data['button_remove'] = $this->language->get('button_remove'); 40 | 41 | if (isset($this->error['warning'])) { 42 | $this->data['error_warning'] = $this->error['warning']; 43 | } else { 44 | $this->data['error_warning'] = ''; 45 | } 46 | 47 | $this->data['breadcrumbs'] = array(); 48 | 49 | $this->data['breadcrumbs'][] = array( 50 | 'text' => $this->language->get('text_home'), 51 | 'href' => $this->url->link('common/home', 'token=' . $this->session->data['token'], 'SSL'), 52 | 'separator' => false 53 | ); 54 | 55 | $this->data['breadcrumbs'][] = array( 56 | 'text' => $this->language->get('text_module'), 57 | 'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'), 58 | 'separator' => ' :: ' 59 | ); 60 | 61 | $this->data['breadcrumbs'][] = array( 62 | 'text' => $this->language->get('heading_title'), 63 | 'href' => $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'), 64 | 'separator' => ' :: ' 65 | ); 66 | 67 | $this->data['action'] = $this->url->link('module/db_cache', 'token=' . $this->session->data['token'], 'SSL'); 68 | 69 | $this->data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'); 70 | 71 | $this->data['modules'] = array(); 72 | 73 | $this->load->model('design/layout'); 74 | 75 | $this->data['layouts'] = $this->model_design_layout->getLayouts(); 76 | 77 | if (isset($this->request->post['db_cache_status'])) { 78 | $this->data['db_cache_status'] = $this->request->post['db_cache_status']; 79 | } else if ($this->config->get('db_cache_status')) { 80 | $this->data['db_cache_status'] = $this->config->get('db_cache_status'); 81 | } 82 | 83 | if (isset($this->request->post['db_cache_cacheTimeoutSeconds'])) { 84 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->request->post['db_cache_cacheTimeoutSeconds']; 85 | } else if ($this->config->get('db_cache_cacheTimeoutSeconds')) { 86 | $this->data['db_cache_cacheTimeoutSeconds'] = $this->config->get('db_cache_cacheTimeoutSeconds'); 87 | } 88 | 89 | $this->template = 'module/db_cache.tpl'; 90 | $this->children = array( 91 | 'common/header', 92 | 'common/footer' 93 | ); 94 | 95 | $this->response->setOutput($this->render()); 96 | } 97 | 98 | protected function validate() { 99 | if (!$this->user->hasPermission('modify', 'module/db_cache')) { 100 | $this->error['warning'] = $this->language->get('error_permission'); 101 | } 102 | 103 | if (!$this->request->post['db_cache_cacheTimeoutSeconds'] || $this->request->post['db_cache_cacheTimeoutSeconds'] <= 0) { 104 | $this->error['warning'] = $this->language->get('error_cacheTimeoutSeconds'); 105 | } 106 | 107 | if (!$this->error) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | } 113 | 114 | public function install() { 115 | $this->load->model('setting/setting'); 116 | $this->model_setting_setting->editSetting('db_cache', array('db_cache_cacheTimeoutSeconds' => DbCache::DEFAULT_CACHE_TIMEOUT_SECONDS, 'db_cache_status' => 1)); 117 | } 118 | 119 | public function uninstall() { 120 | $this->load->model('setting/setting'); 121 | $this->model_setting_setting->deleteSetting('db_cache'); 122 | $this->getDbCache()->clear(); 123 | } 124 | 125 | public function clear() { 126 | $dbCache = $this->getDbCache(); 127 | $dbCache->clear(); 128 | 129 | $redirectRoute = 'module/db_cache'; 130 | if (isset($this->request->get['redirectRoute'])) { 131 | $redirectRoute = $this->request->get['redirectRoute']; 132 | } 133 | 134 | $this->redirect($this->url->link($redirectRoute, 'token=' . $this->session->data['token'], 'SSL')); 135 | } 136 | 137 | private function getDbCache() { 138 | return DbCache::getInstance(); 139 | } 140 | } 141 | ?> -------------------------------------------------------------------------------- /dist_oc156x_vqmod/upload/admin/language/english/module/db_cache.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | 5 | 10 | 11 | 12 |
    13 | 14 | 15 |
    16 |
    17 |
    18 |
    19 |

    20 |
    21 |
    22 |
    23 |
    24 | 25 |
    26 | 27 | 28 |
    29 | 30 |
    31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 52 | 53 |
    35 | 36 | $error['cacheTimeoutSeconds'] 37 | 38 |
    54 |
    55 | 56 |
    57 | 58 |
    59 | 60 |
    61 |

    62 |
    63 |
    64 |
    65 | 66 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /dist_oc156x_vqmod/upload/system/library/pnsols/db_cache.php: -------------------------------------------------------------------------------- 1 | get('config')->get('db_cache_cacheTimeoutSeconds'); 34 | if (!$cacheTimeoutSeconds) { 35 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 36 | } 37 | return $cacheTimeoutSeconds; 38 | } 39 | 40 | private $cacheChanged = FALSE; 41 | 42 | public function isChanged() { 43 | return $this->cacheChanged; 44 | } 45 | 46 | /** 47 | * Protected constructor to prevent creating a new instance of the 48 | * *Singleton* via the `new` operator from outside of this class. 49 | */ 50 | protected function __construct() 51 | { 52 | $this->loadCacheFromFile(); 53 | } 54 | 55 | 56 | 57 | /** 58 | * Private clone method to prevent cloning of the instance of the 59 | * *Singleton* instance. 60 | * 61 | * @return void 62 | */ 63 | private function __clone() 64 | { 65 | } 66 | 67 | /** 68 | * Private unserialize method to prevent unserializing of the *Singleton* 69 | * instance. 70 | * 71 | * @return void 72 | */ 73 | private function __wakeup() 74 | { 75 | } 76 | 77 | private $cacheMap = array(); 78 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 79 | 80 | public function clear() { 81 | $dbCache = $this; 82 | $dirPath = $dbCache->getCacheDirPath(); 83 | $files = scandir($dirPath); 84 | foreach ($files as $filePath) { 85 | if ($filePath == '.' || $filePath == '..') continue; 86 | unlink($dirPath.$filePath); 87 | } 88 | rmdir($dirPath); 89 | } 90 | 91 | public function getCacheDirPath() { 92 | $dirPath = DIR_CACHE.'db_cache/'; 93 | if (!file_exists($dirPath)) mkdir($dirPath); 94 | return $dirPath; 95 | } 96 | 97 | private function getCacheMapFilePath() { 98 | 99 | $dir = DIR_DOWNLOAD . 'db_cache/'; 100 | $dbCacheFilePath = $dir.'db_cache.dat'; 101 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 102 | if (file_exists($dir)) rmdir($dir); 103 | 104 | $latestPath = $this->getCacheDirPath(); 105 | //$latestPath .= '_index'; 106 | $latestPath .= '_cache'; 107 | 108 | return $latestPath; 109 | } 110 | 111 | public function loadCacheFromFile() { 112 | $cacheFilePath = $this->getCacheMapFilePath(); 113 | if (!file_exists($cacheFilePath)) 114 | return; 115 | $handle = fopen($cacheFilePath, "r"); 116 | flock($handle, LOCK_SH); 117 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 118 | fclose($handle); 119 | $this->cacheMap = unserialize($cacheSerialized); 120 | } 121 | 122 | public function saveCacheToFile() { 123 | $cacheFilePath = $this->getCacheMapFilePath(); 124 | 125 | $cacheSerialized = serialize($this->cacheMap); 126 | 127 | $handle = fopen($cacheFilePath, 'w'); 128 | flock($handle, LOCK_EX); 129 | fwrite($handle, $cacheSerialized); 130 | fflush($handle); 131 | fclose($handle); 132 | } 133 | 134 | public function addSelectFetchToCache($queryText, $fetchData) { 135 | $this->setCacheEntry($queryText, $fetchData); 136 | } 137 | 138 | public function getCachedSelectFetch($queryText) { 139 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 140 | //$cachedFetchData = $this->getCacheEntryData($queryText); 141 | 142 | return $cachedFetchData; 143 | } 144 | 145 | private function getCachedDataFromCacheMap($queryText) { 146 | 147 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 148 | $cachedEntry = $this->cacheMap[$queryText]; 149 | $cacheTime = $cachedEntry['time']; 150 | $nowTime = date_create(); 151 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 152 | $daysDiffCount = $secondsDiffSpan->format('%a'); 153 | if ($daysDiffCount >= self::getCacheTimeout()) { 154 | $this->removeCacheEntry($queryText); 155 | return null; 156 | } 157 | return $cachedEntry['data']; 158 | } 159 | return null; 160 | } 161 | 162 | public function processModificationQuery($queryText) { 163 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 164 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 165 | foreach ($dbTableNamesInQuery as $dbTableName) { 166 | if (stripos($queryTextKey, $dbTableName)) { 167 | $this->removeCacheEntry($queryTextKey); 168 | //echo 'remove db cache: '.$queryTextKey.'
    '; 169 | } 170 | } 171 | } 172 | } 173 | 174 | private function getCacheEntryFilePath($cacheKey) { 175 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 176 | } 177 | 178 | private function getCacheEntryFileNameByHash($hash) { 179 | return $hash; 180 | } 181 | 182 | private function getCacheEntryData($cacheKey) { 183 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 184 | $nowTime = date_create(); 185 | 186 | $timeModified = date_create(); 187 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 188 | 189 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 190 | $daysDiffCount = $secondsDiffSpan->format('%a'); 191 | if ($daysDiffCount >= self::getCacheTimeout()) { 192 | $this->removeCacheEntry($cacheKey); 193 | return NULL; 194 | } 195 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 196 | 197 | } 198 | 199 | private function removeCacheEntry($queryTextKey) { 200 | if (isset($this->cacheMap[$queryTextKey])) { 201 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 202 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 203 | $this->cacheMap[$queryTextKey] = null; 204 | unset($this->cacheMap[$queryTextKey]); 205 | $this->cacheChanged = TRUE; 206 | } 207 | } 208 | 209 | private function getSelectQueryHash($queryText) { 210 | return md5($queryText); 211 | } 212 | 213 | private function setCacheEntry($cacheKey, $cacheData) { 214 | $cachedTime = date_create(); 215 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 216 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 217 | $this->cacheChanged = TRUE; 218 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 219 | } 220 | 221 | public static function isModificationQuery($queryText) { 222 | //strpos(trim(strtolower($queryText)), "select"); 223 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 224 | //$isModQuery = $startsWithSelect === FALSE; 225 | 226 | //return $isModQuery; 227 | $arReadQueries = array('select', 'show tables', 'show columns'); 228 | foreach ($arReadQueries as $queryRead) { 229 | $striposSelect = stripos(trim($queryText), $queryRead); 230 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 231 | } 232 | return TRUE; 233 | } 234 | 235 | public static function extractDbTableNamesFromQueryText($queryText) { 236 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 237 | return $tableNames; 238 | } 239 | 240 | public static function processDbQuery($db, $sql) { 241 | $dbCache = DbCache::getInstance(); 242 | if (stripos($sql, 'now()')) { 243 | $c = 0; 244 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 245 | } 246 | if (DbCache::isModificationQuery($sql)) { 247 | $dbCache->processModificationQuery($sql); 248 | } else { 249 | if (!Registry::getInstance()->get('config')->get('db_cache_status')) 250 | return $db->queryNonCache($sql); 251 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 252 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 253 | if ($cachedFetch != null) { 254 | //echo 'cached select query: '.$sql; 255 | return $cachedFetch; 256 | } 257 | } else { 258 | return $db->queryNonCache($sql); 259 | } 260 | } 261 | 262 | $freshDbFetch = $db->queryNonCache($sql); 263 | if (!DbCache::isModificationQuery($sql)) { 264 | //echo 'add cache select query: '.$sql; 265 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 266 | } 267 | return $freshDbFetch; 268 | } 269 | 270 | } 271 | 272 | 273 | 274 | ?> -------------------------------------------------------------------------------- /dist_oc156x_vqmod/upload/vqmod/xml/pnsols_dbcache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DB Cache for OC 4 | 1.16.12.14 5 | 2.3.0 6 | PN Solutions 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | get('config')->get('db_cache_status')) { 20 | if (DbCache::isCreated() && DbCache::getInstance()->isChanged()) DbCache::getInstance()->saveCacheToFile(); 21 | } 22 | ]]> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 42 | 43 | 44 | 45 | driver->query($sql);]]> 46 | get('config')->get('db_cache_status')) 48 | return $this->queryNonCache($sql); 49 | 50 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 51 | return DbCache::processDbQuery($this, $sql); 52 | } 53 | return $this->queryNonCache($sql); 54 | } 55 | 56 | public function queryNonCache($sql) { 57 | return $this->driver->query($sql); 58 | ]]> 59 | 60 | 61 | 62 | 63 | 64 | ]]> 65 | Clear DB Cache]]> 66 | 67 | 68 | -------------------------------------------------------------------------------- /dist_oc20x_ocmod/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DB Cache 4 | PNSols_DBCache_Extension_Modification 5 | 2.0 6 | PN Solutions 7 | http://pnsols.com 8 | 9 | 10 | 11 | 12 | isChanged()) DbCache::getInstance()->saveCacheToFile(); 14 | ]]> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | adaptor->query($sql, $params);]]> 25 | adaptor->query($sql, $params); 32 | ]]> 33 | 34 | 35 | -------------------------------------------------------------------------------- /dist_oc20x_ocmod/readme_oc20x.txt: -------------------------------------------------------------------------------- 1 | DB Cache 2 | OpenCart 2.0.x Database Caching module 3 | version 1.16.12.14 4 | 5 | Caches database sql queries to improve performance to 10 times. 6 | Automatically drop affected cache entries after any modification operation: insert/update/delete 7 | 8 | Installation: 9 | 2.0+ 10 | 1. Download the pnsols_dbcache_oc2.ocmod.zip file 11 | 2. Go Extensions/Extension Installer 12 | 3. Upload the file and click Next 13 | 4. It is installed and if you need to disable it - go to the Extensions/Modifications and disable/remove the DB Cache 14 | To update to newer version - remove the older and install a new one using the steps above -------------------------------------------------------------------------------- /dist_oc20x_ocmod/upload/system/library/pnsols/db_cache.php: -------------------------------------------------------------------------------- 1 | get('config')->get('db_cache_cacheTimeoutSeconds'); 35 | if (!$cacheTimeoutSeconds) { 36 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 37 | } 38 | return $cacheTimeoutSeconds; 39 | */ 40 | return self::DEFAULT_CACHE_TIMEOUT_SECONDS; 41 | } 42 | 43 | private $cacheChanged = FALSE; 44 | 45 | public function isChanged() { 46 | return $this->cacheChanged; 47 | } 48 | 49 | /** 50 | * Protected constructor to prevent creating a new instance of the 51 | * *Singleton* via the `new` operator from outside of this class. 52 | */ 53 | protected function __construct() 54 | { 55 | $this->loadCacheFromFile(); 56 | } 57 | 58 | 59 | 60 | /** 61 | * Private clone method to prevent cloning of the instance of the 62 | * *Singleton* instance. 63 | * 64 | * @return void 65 | */ 66 | private function __clone() 67 | { 68 | } 69 | 70 | /** 71 | * Private unserialize method to prevent unserializing of the *Singleton* 72 | * instance. 73 | * 74 | * @return void 75 | */ 76 | private function __wakeup() 77 | { 78 | } 79 | 80 | private $cacheMap = array(); 81 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 82 | 83 | public function clear() { 84 | $dbCache = $this; 85 | $dirPath = $dbCache->getCacheDirPath(); 86 | $files = scandir($dirPath); 87 | foreach ($files as $filePath) { 88 | if ($filePath == '.' || $filePath == '..') continue; 89 | unlink($dirPath.$filePath); 90 | } 91 | rmdir($dirPath); 92 | } 93 | 94 | public function getCacheDirPath() { 95 | $dirPath = DIR_CACHE.'db_cache/'; 96 | if (!file_exists($dirPath)) mkdir($dirPath); 97 | return $dirPath; 98 | } 99 | 100 | private function getCacheMapFilePath() { 101 | 102 | $dir = DIR_DOWNLOAD . 'db_cache/'; 103 | $dbCacheFilePath = $dir.'db_cache.dat'; 104 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 105 | if (file_exists($dir)) rmdir($dir); 106 | 107 | $latestPath = $this->getCacheDirPath(); 108 | //$latestPath .= '_index'; 109 | $latestPath .= '_cache'; 110 | 111 | return $latestPath; 112 | } 113 | 114 | public function loadCacheFromFile() { 115 | $cacheFilePath = $this->getCacheMapFilePath(); 116 | if (!file_exists($cacheFilePath)) 117 | return; 118 | $handle = fopen($cacheFilePath, "r"); 119 | flock($handle, LOCK_SH); 120 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 121 | fclose($handle); 122 | $this->cacheMap = unserialize($cacheSerialized); 123 | } 124 | 125 | public function saveCacheToFile() { 126 | $cacheFilePath = $this->getCacheMapFilePath(); 127 | 128 | $cacheSerialized = serialize($this->cacheMap); 129 | 130 | $handle = fopen($cacheFilePath, 'w'); 131 | flock($handle, LOCK_EX); 132 | fwrite($handle, $cacheSerialized); 133 | fflush($handle); 134 | fclose($handle); 135 | } 136 | 137 | public function addSelectFetchToCache($queryText, $fetchData) { 138 | $this->setCacheEntry($queryText, $fetchData); 139 | } 140 | 141 | public function getCachedSelectFetch($queryText) { 142 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 143 | //$cachedFetchData = $this->getCacheEntryData($queryText); 144 | 145 | return $cachedFetchData; 146 | } 147 | 148 | private function getCachedDataFromCacheMap($queryText) { 149 | 150 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 151 | $cachedEntry = $this->cacheMap[$queryText]; 152 | $cacheTime = $cachedEntry['time']; 153 | $nowTime = date_create(); 154 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 155 | $daysDiffCount = $secondsDiffSpan->format('%a'); 156 | if ($daysDiffCount >= self::getCacheTimeout()) { 157 | $this->removeCacheEntry($queryText); 158 | return null; 159 | } 160 | return $cachedEntry['data']; 161 | } 162 | return null; 163 | } 164 | 165 | public function processModificationQuery($queryText) { 166 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 167 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 168 | foreach ($dbTableNamesInQuery as $dbTableName) { 169 | if (stripos($queryTextKey, $dbTableName)) { 170 | $this->removeCacheEntry($queryTextKey); 171 | //echo 'remove db cache: '.$queryTextKey.'
    '; 172 | } 173 | } 174 | } 175 | } 176 | 177 | private function getCacheEntryFilePath($cacheKey) { 178 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 179 | } 180 | 181 | private function getCacheEntryFileNameByHash($hash) { 182 | return $hash; 183 | } 184 | 185 | private function getCacheEntryData($cacheKey) { 186 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 187 | $nowTime = date_create(); 188 | 189 | $timeModified = date_create(); 190 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 191 | 192 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 193 | $daysDiffCount = $secondsDiffSpan->format('%a'); 194 | if ($daysDiffCount >= self::getCacheTimeout()) { 195 | $this->removeCacheEntry($cacheKey); 196 | return NULL; 197 | } 198 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 199 | 200 | } 201 | 202 | private function removeCacheEntry($queryTextKey) { 203 | if (isset($this->cacheMap[$queryTextKey])) { 204 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 205 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 206 | $this->cacheMap[$queryTextKey] = null; 207 | unset($this->cacheMap[$queryTextKey]); 208 | $this->cacheChanged = TRUE; 209 | } 210 | } 211 | 212 | private function getSelectQueryHash($queryText) { 213 | return md5($queryText); 214 | } 215 | 216 | private function setCacheEntry($cacheKey, $cacheData) { 217 | $cachedTime = date_create(); 218 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 219 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 220 | $this->cacheChanged = TRUE; 221 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 222 | } 223 | 224 | public static function isModificationQuery($queryText) { 225 | //strpos(trim(strtolower($queryText)), "select"); 226 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 227 | //$isModQuery = $startsWithSelect === FALSE; 228 | 229 | //return $isModQuery; 230 | $arReadQueries = array('select', 'show tables', 'show columns'); 231 | foreach ($arReadQueries as $queryRead) { 232 | $striposSelect = stripos(trim($queryText), $queryRead); 233 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 234 | } 235 | return TRUE; 236 | } 237 | 238 | public static function extractDbTableNamesFromQueryText($queryText) { 239 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 240 | return $tableNames; 241 | } 242 | 243 | public static function processDbQuery($db, $sql, $params = NULL) { 244 | if ($params === NULL) $params = array(); 245 | $dbCache = DbCache::getInstance(); 246 | if (stripos($sql, 'now()')) { 247 | $c = 0; 248 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 249 | } 250 | if (DbCache::isModificationQuery($sql)) { 251 | $dbCache->processModificationQuery($sql); 252 | } else { 253 | //if (!Registry::getInstance()->get('config')->get('db_cache_status')) 254 | //return $db->queryNonCache($sql); 255 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 256 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 257 | if ($cachedFetch != null) { 258 | //echo 'cached select query: '.$sql; 259 | return $cachedFetch; 260 | } 261 | } else { 262 | return $db->queryNonCache($sql); 263 | } 264 | } 265 | 266 | $freshDbFetch = $db->queryNonCache($sql); 267 | if (!DbCache::isModificationQuery($sql)) { 268 | //echo 'add cache select query: '.$sql; 269 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 270 | } 271 | return $freshDbFetch; 272 | } 273 | 274 | } 275 | 276 | 277 | 278 | ?> -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | DB Cache 2 | opencart module 3 | version 1.16.12.14 4 | ——————— 5 | 6 | VQMOD required for 1.5.6.x 7 | 2.0.x doesn't require VQMOD - there is ocmod 8 | 9 | Caches db sql queries to improve perfomance up to 10 times. 10 | Automatically drop affected cache entries after any modification operation: insert/update/delete 11 | 12 | CHANGES: 13 | 1.16.12.14: 14 | - sql query text is adjusted for mysql query cache 15 | 1.16.08.10: 16 | - Performance is boosted up - db cache kernel is optimized 17 | - Module settings - Cache Timeout Seconds to set for cache entries and Module Status - you could just disable db caching at any moment then turn on again 18 | - Clear DB Cache button is added to Admin System menu 19 | - Small bugs are fixed 20 | 21 | Installation: 22 | ——————- 23 | 1. Copy upload folder content to the root of your site. 24 | 2. Install DB Cache module 25 | 3. Enjoy perfomance boost 26 | 27 | tested on opencart 1.5.6.3 28 | 29 | PN Solutions http://pnsols.com 2016 30 | info@pnsols.com 31 | -------------------------------------------------------------------------------- /src/db_cache.php: -------------------------------------------------------------------------------- 1 | get('config')->get('db_cache_cacheTimeoutSeconds'); 34 | if (!$cacheTimeoutSeconds) { 35 | $cacheTimeoutSeconds = self::DEFAULT_CACHE_TIMEOUT_SECONDS; 36 | } 37 | return $cacheTimeoutSeconds; 38 | } 39 | 40 | private $cacheChanged = FALSE; 41 | 42 | public function isChanged() { 43 | return $this->cacheChanged; 44 | } 45 | 46 | /** 47 | * Protected constructor to prevent creating a new instance of the 48 | * *Singleton* via the `new` operator from outside of this class. 49 | */ 50 | protected function __construct() 51 | { 52 | $this->loadCacheFromFile(); 53 | } 54 | 55 | 56 | 57 | /** 58 | * Private clone method to prevent cloning of the instance of the 59 | * *Singleton* instance. 60 | * 61 | * @return void 62 | */ 63 | private function __clone() 64 | { 65 | } 66 | 67 | /** 68 | * Private unserialize method to prevent unserializing of the *Singleton* 69 | * instance. 70 | * 71 | * @return void 72 | */ 73 | private function __wakeup() 74 | { 75 | } 76 | 77 | private $cacheMap = array(); 78 | const DEFAULT_CACHE_TIMEOUT_SECONDS = 3600; 79 | 80 | public function clear() { 81 | $dbCache = $this; 82 | $dirPath = $dbCache->getCacheDirPath(); 83 | $files = scandir($dirPath); 84 | foreach ($files as $filePath) { 85 | if ($filePath == '.' || $filePath == '..') continue; 86 | unlink($dirPath.$filePath); 87 | } 88 | rmdir($dirPath); 89 | } 90 | 91 | public function getCacheDirPath() { 92 | $dirPath = DIR_CACHE.'db_cache/'; 93 | if (!file_exists($dirPath)) mkdir($dirPath); 94 | return $dirPath; 95 | } 96 | 97 | private function getCacheMapFilePath() { 98 | 99 | $dir = DIR_DOWNLOAD . 'db_cache/'; 100 | $dbCacheFilePath = $dir.'db_cache.dat'; 101 | if (file_exists($dbCacheFilePath)) unlink($dbCacheFilePath); 102 | if (file_exists($dir)) rmdir($dir); 103 | 104 | $latestPath = $this->getCacheDirPath(); 105 | //$latestPath .= '_index'; 106 | $latestPath .= '_cache'; 107 | 108 | return $latestPath; 109 | } 110 | 111 | public function loadCacheFromFile() { 112 | $cacheFilePath = $this->getCacheMapFilePath(); 113 | if (!file_exists($cacheFilePath)) 114 | return; 115 | $handle = fopen($cacheFilePath, "r"); 116 | flock($handle, LOCK_SH); 117 | $cacheSerialized = fread($handle, filesize($cacheFilePath)); 118 | fclose($handle); 119 | $this->cacheMap = unserialize($cacheSerialized); 120 | } 121 | 122 | public function saveCacheToFile() { 123 | $cacheFilePath = $this->getCacheMapFilePath(); 124 | 125 | $cacheSerialized = serialize($this->cacheMap); 126 | 127 | $handle = fopen($cacheFilePath, 'w'); 128 | flock($handle, LOCK_EX); 129 | fwrite($handle, $cacheSerialized); 130 | fflush($handle); 131 | fclose($handle); 132 | } 133 | 134 | public function addSelectFetchToCache($queryText, $fetchData) { 135 | $this->setCacheEntry($queryText, $fetchData); 136 | } 137 | 138 | public function getCachedSelectFetch($queryText) { 139 | $cachedFetchData = $this->getCachedDataFromCacheMap($queryText); 140 | //$cachedFetchData = $this->getCacheEntryData($queryText); 141 | 142 | return $cachedFetchData; 143 | } 144 | 145 | private function getCachedDataFromCacheMap($queryText) { 146 | 147 | if (isset($this->cacheMap[$queryText]) && $this->cacheMap[$queryText] != null) { 148 | $cachedEntry = $this->cacheMap[$queryText]; 149 | $cacheTime = $cachedEntry['time']; 150 | $nowTime = date_create(); 151 | $secondsDiffSpan = date_diff($cacheTime, $nowTime); 152 | $daysDiffCount = $secondsDiffSpan->format('%a'); 153 | if ($daysDiffCount >= self::getCacheTimeout()) { 154 | $this->removeCacheEntry($queryText); 155 | return null; 156 | } 157 | return $cachedEntry['data']; 158 | } 159 | return null; 160 | } 161 | 162 | public function processModificationQuery($queryText) { 163 | $dbTableNamesInQuery = DbCache::extractDbTableNamesFromQueryText($queryText); 164 | foreach ($this->cacheMap as $queryTextKey => $cacheEntry) { 165 | foreach ($dbTableNamesInQuery as $dbTableName) { 166 | if (stripos($queryTextKey, $dbTableName)) { 167 | $this->removeCacheEntry($queryTextKey); 168 | //echo 'remove db cache: '.$queryTextKey.'
    '; 169 | } 170 | } 171 | } 172 | } 173 | 174 | private function getCacheEntryFilePath($cacheKey) { 175 | return $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->getSelectQueryHash($cacheKey)); 176 | } 177 | 178 | private function getCacheEntryFileNameByHash($hash) { 179 | return $hash; 180 | } 181 | 182 | private function getCacheEntryData($cacheKey) { 183 | if (!file_exists($this->getCacheEntryFilePath($cacheKey))) return NULL; 184 | $nowTime = date_create(); 185 | 186 | $timeModified = date_create(); 187 | date_timestamp_set($timeModified, filemtime($this->getCacheEntryFilePath($cacheKey))); 188 | 189 | $secondsDiffSpan = date_diff($timeModified, $nowTime); 190 | $daysDiffCount = $secondsDiffSpan->format('%a'); 191 | if ($daysDiffCount >= self::getCacheTimeout()) { 192 | $this->removeCacheEntry($cacheKey); 193 | return NULL; 194 | } 195 | return unserialize(file_get_contents($this->getCacheEntryFilePath($cacheKey))); 196 | 197 | } 198 | 199 | private function removeCacheEntry($queryTextKey) { 200 | if (isset($this->cacheMap[$queryTextKey])) { 201 | //$cacheEntryFilePath = $this->getCacheDirPath().$this->getCacheEntryFileNameByHash($this->cacheMap[$queryTextKey]); 202 | //if (file_exists($cacheEntryFilePath)) unlink($cacheEntryFilePath); 203 | $this->cacheMap[$queryTextKey] = null; 204 | unset($this->cacheMap[$queryTextKey]); 205 | $this->cacheChanged = TRUE; 206 | } 207 | } 208 | 209 | private function getSelectQueryHash($queryText) { 210 | return md5($queryText); 211 | } 212 | 213 | private function setCacheEntry($cacheKey, $cacheData) { 214 | $cachedTime = date_create(); 215 | //file_put_contents($this->getCacheEntryFilePath($cacheKey), serialize($cacheData)); 216 | $this->cacheMap[$cacheKey] = array('time' => $cachedTime, 'data' => $cacheData); 217 | $this->cacheChanged = TRUE; 218 | //$this->cacheMap[$cacheKey] = $this->getSelectQueryHash($cacheKey); 219 | } 220 | 221 | public static function isModificationQuery($queryText) { 222 | //strpos(trim(strtolower($queryText)), "select"); 223 | //$startsWithSelect = $selectStrPos === 0 || $selectStrPos === '0'; 224 | //$isModQuery = $startsWithSelect === FALSE; 225 | 226 | //return $isModQuery; 227 | $arReadQueries = array('select', 'show tables', 'show columns'); 228 | foreach ($arReadQueries as $queryRead) { 229 | $striposSelect = stripos(trim($queryText), $queryRead); 230 | if ($striposSelect === 0 || $striposSelect === '0') return FALSE; 231 | } 232 | return TRUE; 233 | } 234 | 235 | public static function extractDbTableNamesFromQueryText($queryText) { 236 | $tableNames = preg_grep('/'.DB_PREFIX.'.+/', explode(' ', $queryText)); 237 | return $tableNames; 238 | } 239 | 240 | public static function processDbQuery($db, $sql) { 241 | $dbCache = DbCache::getInstance(); 242 | if (stripos($sql, 'now()')) { 243 | $c = 0; 244 | $sql = str_ireplace('NOW()', '\''.date('Y-m-d H:i').':00\'', $sql, $c); 245 | } 246 | if (DbCache::isModificationQuery($sql)) { 247 | $dbCache->processModificationQuery($sql); 248 | } else { 249 | if (!Registry::getInstance()->get('config')->get('db_cache_status')) 250 | return $db->queryNonCache($sql); 251 | if (!stripos($_SERVER['REQUEST_URI'], '/admin')) { 252 | $cachedFetch = $dbCache->getCachedSelectFetch($sql); 253 | if ($cachedFetch != null) { 254 | //echo 'cached select query: '.$sql; 255 | return $cachedFetch; 256 | } 257 | } else { 258 | return $db->queryNonCache($sql); 259 | } 260 | } 261 | 262 | $freshDbFetch = $db->queryNonCache($sql); 263 | if (!DbCache::isModificationQuery($sql)) { 264 | //echo 'add cache select query: '.$sql; 265 | $dbCache->addSelectFetchToCache($sql, $freshDbFetch); 266 | } 267 | return $freshDbFetch; 268 | } 269 | 270 | } 271 | 272 | 273 | 274 | ?> -------------------------------------------------------------------------------- /zip/pnsols_opencart_db_cache_156x_v1160810.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PNSolutions/opencart-database-cache-extension/a081b778a1f54c638184324741cd63bd4a9ff1ad/zip/pnsols_opencart_db_cache_156x_v1160810.zip -------------------------------------------------------------------------------- /zip/pnsols_opencart_db_cache_156x_v1161214.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PNSolutions/opencart-database-cache-extension/a081b778a1f54c638184324741cd63bd4a9ff1ad/zip/pnsols_opencart_db_cache_156x_v1161214.zip -------------------------------------------------------------------------------- /zip/pnsols_opencart_db_cache_20x.ocmod.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PNSolutions/opencart-database-cache-extension/a081b778a1f54c638184324741cd63bd4a9ff1ad/zip/pnsols_opencart_db_cache_20x.ocmod.zip --------------------------------------------------------------------------------