Index: branches/5.1.x/core/kernel/application.php
===================================================================
diff -u -N -r13086 -r13168
--- branches/5.1.x/core/kernel/application.php (.../application.php) (revision 13086)
+++ branches/5.1.x/core/kernel/application.php (.../application.php) (revision 13168)
@@ -1,6 +1,6 @@
Application->ConfigValue('MemcachedServers');
-
- if ($memcached_servers && class_exists('Memcache')) {
- $this->Memcached = new Memcache();
- $servers = explode(';', $memcached_servers);
- foreach ($servers as $server) {
- list ($server, $port) = strpos($server, ':') !== false ? explode(':', $server, 2) : Array ($server, 11211);
- $this->Memcached->addServer($server, $port);
- }
- }
-
- //try to set something to cache, if not working - set $this->Memcached to null
- }
-
- function CacheSet($name, $value, $expiration)
- {
- if (isset($this->Memcached)) {
- return $this->Memcached->set($name, $value, 0, $expiration);
- }
- return false;
- }
-
- function CacheGet($name)
- {
- if (isset($this->Memcached)) {
- return $this->Memcached->get($name);
- }
- return false;
- }
-
/**
* Initializes the Application
*
@@ -305,8 +271,6 @@
$this->isAdmin = constOn('ADMIN');
- $this->InitMemcached();
-
if (!constOn('SKIP_OUT_COMPRESSION')) {
ob_start(); // collect any output from method (other then tags) into buffer
}
@@ -333,6 +297,7 @@
$this->Factory = new kFactory();
$this->registerDefaultClasses();
$this->Phrases = new PhrasesCache();
+ $this->memoryCache =& $this->Factory->makeClass('Cache');
$this->EventManager =& $this->Factory->makeClass('EventManager');
$this->Factory->Storage['EventManager'] =& $this->EventManager;
$this->RegisterDefaultBuildEvents();
@@ -386,34 +351,32 @@
$this->LoadCache();
$this->InitConfig();
- $this->Phrases->Init('phrases');
-
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Loaded cache and phrases');
}
- $this->ValidateLogin();
+ $this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
$this->UnitConfigReader->AfterConfigRead();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Processed AfterConfigRead');
}
- /*// Module items are recalled during url parsing & PhrasesCache is needed already there,
- // because it's used in their build events. That's why phrases cache initialization is
- // called from kHTTPQuery in case when mod_rewrite is used
- if (!$this->RewriteURLs()) {
- $this->Phrases = new PhrasesCache();
- }*/
+ if ($this->GetVar('m_cat_id') === false) {
+ $this->SetVar('m_cat_id', 0);
+ }
- if ($this->GetVar('m_cat_id') === false) $this->SetVar('m_cat_id', 0);
if (!$this->RecallVar('curr_iso')) {
$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
}
- $this->SetVar('visits_id', $this->RecallVar('visit_id') );
+ $visit_id = $this->RecallVar('visit_id');
+ if ($visit_id !== false) {
+ $this->SetVar('visits_id', $visit_id);
+ }
+
$language =& $this->recallObject( 'lang.current', null, Array('live_table' => true) );
if (preg_match('/utf-8/', $language->GetDBField('Charset'))) {
setlocale(LC_ALL, 'en_US.UTF-8');
@@ -494,8 +457,8 @@
$language_id = 'default';
}
- $this->SetVar('lang.current_id', $language_id );
- $this->SetVar('m_lang', $language_id );
+ $this->SetVar('lang.current_id', $language_id);
+ $this->SetVar('m_lang', $language_id);
$lang_mode = $this->GetVar('lang_mode');
$this->SetVar('lang_mode', '');
@@ -567,17 +530,23 @@
function GetDefaultLanguageId($init = false)
{
- static $language_info = null;
+ $cache_key = 'primary_language_info[%LangSerial%]';
+ $language_info = $this->getCache($cache_key);
- if (!isset($language_info)) {
+ if ($language_info === false) {
// cache primary language info first
$table = $this->getUnitOption('lang', 'TableName');
$id_field = $this->getUnitOption('lang', 'IDField');
+ $this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
FROM ' . $table . '
WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
+
+ if ($language_info !== false) {
+ $this->setCache($cache_key, $language_info);
+ }
}
$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
@@ -611,26 +580,46 @@
$theme_id = 999;
}
else {
- $table = $this->getUnitOption('theme','TableName');
- $id_field = $this->getUnitOption('theme','IDField');
- $sql = 'SELECT '.$id_field.'
- FROM '.$table.'
- WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
- $theme_id = $this->Conn->GetOne($sql);
+ $cache_key = 'primary_theme[%ThemeSerial%]';
+ $theme_id = $this->getCache($cache_key);
+
+ if ($theme_id === false) {
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
+ FROM ' . $this->getUnitOption('theme', 'TableName') . '
+ WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
+ $theme_id = $this->Conn->GetOne($sql);
+
+ if ($theme_id !== false) {
+ $this->setCache($cache_key, $theme_id);
+ }
+ }
}
return $theme_id;
}
function GetPrimaryCurrency()
{
- if ($this->isModuleEnabled('In-Commerce')) {
- $table = $this->getUnitOption('curr', 'TableName');
- return $this->Conn->GetOne('SELECT ISO FROM '.$table.' WHERE IsPrimary = 1');
+ $cache_key = 'primary_currency[%CurrSerial%]';
+ $primary_currency = $this->getCache($cache_key);
+
+ if ($primary_currency === false) {
+ if ($this->isModuleEnabled('In-Commerce')) {
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT ISO
+ FROM ' . $this->getUnitOption('curr', 'TableName') . '
+ WHERE IsPrimary = 1';
+ $primary_currency = $this->Conn->GetOne($sql);
+ }
+ else {
+ $primary_currency = 'USD';
+ }
+
+ $this->setCache($cache_key, $primary_currency);
}
- else {
- return 'USD';
- }
+
+ return $primary_currency;
}
/**
@@ -693,99 +682,231 @@
}
/**
- * Returns item's filename that corresponds id passed. If possible, then get it from cache
+ * Returns cached category informaton by given cache name. All given category
+ * information is recached, when at least one of 4 caches is missing.
*
- * @param string $prefix
- * @param int $id
+ * @param int $category_id
+ * @param string $name cache name = {filenames, category_designs, category_tree}
* @return string
*/
- function getFilename($prefix, $id, $category_id=null)
+ function getCategoryCache($category_id, $name)
{
- $filename = $this->getCache('filenames', $prefix.'_'.$id);
- if ($filename === false) {
- $table = $this->getUnitOption($prefix, 'TableName');
- $id_field = $this->getUnitOption($prefix, 'IDField');
+ $serial_name = '[%CIDSerial:' . $category_id . '%]';
+ $cache_key = $name . $serial_name;
+ $ret = $this->getCache($cache_key);
- if ($prefix == 'c') {
- if(!$id) {
- $this->setCache('filenames', $prefix.'_'.$id, '');
- return '';
- }
+ if ($ret === false) {
+ if (!$category_id) {
+ // don't query database for "Home" category (ID = 0), because it doesn't exist in database
+ return false;
+ }
- // this allows to save 2 sql queries for each category
- $sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
- FROM '.$table.'
- WHERE '.$id_field.' = '.$this->Conn->qstr($id);
- $category_data = $this->Conn->GetRow($sql);
+ // this allows to save 2 sql queries for each category
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
+ FROM ' . TABLE_PREFIX . 'Category
+ WHERE CategoryId = ' . (int)$category_id;
+ $category_data = $this->Conn->GetRow($sql);
+ if ($category_data !== false) {
// only direct links to category pages work (symlinks, container pages and so on won't work)
- $filename = $category_data['NamedParentPath'];
- $this->setCache('category_templates', $id, $filename);
- $this->setCache('category_designs', $id, ltrim($category_data['CachedTemplate'], '/'));
- $this->setCache('category_tree', $id, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
+ $this->setCache('filenames' . $serial_name, $category_data['NamedParentPath']);
+ $this->setCache('category_designs' . $serial_name, ltrim($category_data['CachedTemplate'], '/'));
+ $this->setCache('category_tree' . $serial_name, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
}
- else {
- $sql = 'SELECT ResourceId
- FROM ' . $table . '
- WHERE ' . $id_field . ' = ' . $this->Conn->qstr($id);
- $resource_id = $this->Conn->GetOne($sql);
+ }
- if (is_null($category_id)) {
- $category_id = $this->GetVar('m_cat_id');
- }
+ return $this->getCache($cache_key);
+ }
- $sql = 'SELECT Filename
- FROM ' . TABLE_PREFIX . 'CategoryItems
- WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')';
- $filename = $this->Conn->GetOne($sql);
+ /**
+ * Returns item's filename that corresponds id passed. If possible, then get it from cache
+ *
+ * @param string $prefix
+ * @param int $id
+ * @param int $category_id
+ * @return string
+ */
+ function getFilename($prefix, $id, $category_id = null)
+ {
+ if ($prefix == 'c') {
+ trigger_error('Method "' . __FUNCTION__ . '" no longer work with "c" prefix. Please use "getCategoryCache" method instead.', E_USER_ERROR);
+ return false;
+ }
- /*if (!$filename) {
- $sql = 'SELECT Filename
- FROM ' . TABLE_PREFIX . 'CategoryItems
- WHERE ItemResourceId = ' . $resource_id . ' AND PrimaryCat = 1';
- $filename = $this->Conn->GetOne($sql);
- }
+ $category_id = isset($category_id) ? $category_id : $this->GetVar('m_cat_id');
- $sql = 'SELECT Filename
- FROM ' . $table . '
- WHERE ' . $id_field . ' = ' . $this->Conn->qstr($id);
- $filename = $this->Conn->GetOne($sql);*/
+ $cache_key = 'filenames[%' . $this->incrementCacheSerial($prefix, $id, false) . '%]:' . (int)$category_id;
+ $filename = $this->getCache($cache_key);
+
+ if ($filename === false) {
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT ResourceId
+ FROM ' . $this->getUnitOption($prefix, 'TableName') . '
+ WHERE ' . $this->getUnitOption($prefix, 'IDField') . ' = ' . $this->Conn->qstr($id);
+ $resource_id = $this->Conn->GetOne($sql);
+
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT Filename
+ FROM ' . TABLE_PREFIX . 'CategoryItems
+ WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')';
+ $filename = $this->Conn->GetOne($sql);
+
+ if ($filename !== false) {
+ $this->setCache($cache_key, $filename);
}
- $this->setCache('filenames', $prefix.'_'.$id, $filename);
}
+
return $filename;
}
+ /**
+ * Returns caching type (none, memory, temporary)
+ *
+ * @return int
+ */
+ function isCachingType($caching_type)
+ {
+ return $this->memoryCache->getCachingType() == $caching_type;
+ }
/**
+ * Increments serial based on prefix and it's ID (optional)
+ *
+ * @param string $prefix
+ * @param int $id ID (value of IDField) or ForeignKeyField:ID
+ * @param bool $increment
+ */
+ function incrementCacheSerial($prefix, $id = null, $increment = true)
+ {
+ $pascal_case_prefix = implode('', array_map('ucfirst', explode('-', $prefix)));
+ $serial_name = $pascal_case_prefix . (isset($id) ? 'IDSerial:' . $id : 'Serial');
+
+ if ($increment) {
+ if (defined('DEBUG_MODE') && DEBUG_MODE && $this->isDebugMode()) {
+ $this->Application->Debugger->appendHTML('Incrementing serial: ' . $serial_name . '.');
+ }
+
+ if ($this->isCachingType(CACHING_TYPE_MEMORY)) {
+ $this->setCache($serial_name, (int)$this->getCache($serial_name) + 1);
+ }
+
+ // delete cached mod-rewrite urls related to given prefix and id
+ $delete_clause = isset($id) ? $prefix . ':' . $id : $prefix;
+
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls
+ WHERE Prefixes LIKE ' . $this->Conn->qstr('%|' . $delete_clause . '|%');
+ $this->Conn->Query($sql);
+ }
+
+ return $serial_name;
+ }
+
+ /**
* Adds new value to cache $cache_name and identified by key $key
*
- * @param string $cache_name cache name
* @param int $key key name to add to cache
* @param mixed $value value of chached record
+ * @param int $expiration when value expires (0 - doesn't expire)
*/
- function setCache($cache_name, $key, $value, $expiration=3600)
+ function setCache($key, $value, $expiration = 0)
{
- $cache =& $this->recallObject('Cache');
- /* @var $cache kCache */
+ return $this->memoryCache->setCache($key, $value, $expiration);
+ }
- return $cache->setCache($cache_name, $key, $value, $expiration);
+ /**
+ * Sets value to database cache
+ *
+ * @param string $name
+ * @param mixed $value
+ * @param int $expiration
+ */
+ function setDBCache($name, &$value, $expiration = false)
+ {
+ if ((int)$expiration <= 0) {
+ $expiration = -1;
+ }
+
+ $fields_hash = Array (
+ 'VarName' => $name,
+ 'Data' => &$value,
+ 'Cached' => adodb_mktime(),
+ 'LifeTime' => (int)$expiration,
+ );
+
+ $this->Conn->nextQueryCachable = true;
+ $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Cache', 'REPLACE');
}
/**
* Returns cached $key value from cache named $cache_name
*
- * @param string $cache_name cache name
* @param int $key key name from cache
+ * @param bool $store_locally store data locally after retrieved
* @return mixed
*/
- function getCache($cache_name, $key)
+ function getCache($key, $store_locally = true)
{
- $cache =& $this->recallObject('Cache');
- return $cache->getCache($cache_name, $key);
+ return $this->memoryCache->getCache($key, $store_locally);
}
/**
+ * Returns value from database cache
+ *
+ * @param string $name key name
+ * @return mixed
+ */
+ function getDBCache($name)
+ {
+ $this->Conn->nextQueryCachable = true;
+
+ $sql = 'SELECT Data, Cached, LifeTime
+ FROM ' . TABLE_PREFIX . 'Cache
+ WHERE VarName = ' . $this->Conn->qstr($name);
+ $data = $this->Conn->GetRow($sql);
+
+ if ($data) {
+ $lifetime = (int)$data['LifeTime']; // in seconds
+ if (($lifetime > 0) && ($data['Cached'] + $lifetime < adodb_mktime())) {
+ // delete expired
+ $this->Conn->nextQueryCachable = true;
+
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Cache
+ WHERE VarName = ' . $this->Conn->qstr($name);
+ $this->Conn->Query($sql);
+
+ return false;
+ }
+
+ return $data['Data'];
+ }
+
+ return false;
+ }
+
+ /**
+ * Deletes key from cache
+ *
+ * @param string $key
+ */
+ function deleteCache($key)
+ {
+ $this->memoryCache->delete($key);
+ }
+
+ /**
+ * Deletes key from database cache
+ *
+ * @param string $name
+ */
+ function deleteDBCache($name)
+ {
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Cache
+ WHERE VarName = ' . $this->Conn->qstr($name);
+ $this->Conn->Query($sql);
+ }
+
+ /**
* Defines default constants if it's not defined before - in config.php
*
* @access private
@@ -805,13 +926,18 @@
k4_include_once(KERNEL_PATH.'/constants.php');
}
- if (!$this->ModuleInfo) return false;
- foreach($this->ModuleInfo as $module_name => $module_info)
- {
- $module_path = '/'.$module_info['Path'];
- $contants_file = FULL_PATH.$module_path.'constants.php';
- if( file_exists($contants_file) ) k4_include_once($contants_file);
+ if (!$this->ModuleInfo) {
+ return false;
}
+
+ foreach ($this->ModuleInfo as $module_name => $module_info) {
+ $contants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
+
+ if (file_exists($contants_file)) {
+ k4_include_once($contants_file);
+ }
+ }
+
return true;
}
@@ -937,8 +1063,7 @@
$this->Session->SaveData();
if (constOn('DBG_CACHE')) {
- $cache =& $this->recallObject('Cache');
- $cache->printStatistics();
+ $this->memoryCache->printStatistics();
}
$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
@@ -1411,7 +1536,8 @@
$target_url = 'http://'.DOMAIN.$this->ConfigValue('Site_Path');
}
- if (!preg_match('#'.preg_quote($cookie_url).'#', $target_url)) {
+ // set session to GET_ONLY, to pass sid only if sid is REAL AND session is set
+ if (!preg_match('#'.preg_quote($cookie_url).'#', $target_url) && $session->SessionSet) {
$session->SetMode(smGET_ONLY);
}
}
@@ -1847,17 +1973,15 @@
}
if (strtolower($t) == '__default__') {
- // to put category & item templates into cache
- $filename = $this->getFilename('c', $category_id);
if (is_numeric($item_id)) {
$mod_rw_helper =& $this->Application->recallObject('ModRewriteHelper');
/* @var $mod_rw_helper kModRewriteHelper */
$t = $mod_rw_helper->GetItemTemplate($category_id, $pass_element); // $pass_element should be the last processed element
-// $t = $this->getCache('item_templates', $category_id);
+ // $t = $this->getCategoryCache($category_id, 'item_templates');
}
elseif ($category_id) {
- $t = strtolower( preg_replace('/^Content\//i', '', $this->getCache('category_templates', $category_id)) );
+ $t = strtolower(preg_replace('/^Content\//i', '', $this->getCategoryCache($category_id, 'filenames') ));
}
else {
$t = 'index';
@@ -2057,39 +2181,44 @@
$this->Session->LoadPersistentVars();
}
- function LoadCache() {
- $cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->isAdmin;
- $query = sprintf("SELECT PhraseList, ConfigVariables FROM %s WHERE Template = %s",
- TABLE_PREFIX.'PhraseCache',
- $this->Conn->qstr(md5($cache_key)));
- $res = $this->Conn->GetRow($query);
+ function LoadCache()
+ {
+ // TODO: maybe language part isn't required, since same phrase from different languages have one ID now
+ $cache_key = $this->GetVar('t') . $this->GetVar('m_theme') . $this->GetVar('m_lang') . $this->isAdmin;
+ $sql = 'SELECT PhraseList, ConfigVariables
+ FROM ' . TABLE_PREFIX . 'PhraseCache
+ WHERE Template = ' . $this->Conn->qstr( md5($cache_key) );
+ $res = $this->Conn->GetRow($sql);
+
if ($res) {
- $this->Caches['PhraseList'] = $res['PhraseList'] ? explode(',', $res['PhraseList']) : array();
+ $this->Caches['PhraseList'] = $res['PhraseList'] ? explode(',', $res['PhraseList']) : Array ();
+ $config_ids = $res['ConfigVariables'] ? explode(',', $res['ConfigVariables']) : Array ();
- $config_ids = $res['ConfigVariables'] ? explode(',', $res['ConfigVariables']) : array();
if (isset($this->Caches['ConfigVariables'])) {
$config_ids = array_diff($config_ids, $this->Caches['ConfigVariables']);
}
}
else {
- $config_ids = array();
+ $config_ids = Array ();
}
+
+ $this->Phrases->Init('phrases');
$this->Caches['ConfigVariables'] = $config_ids;
$this->ConfigCacheIds = $config_ids;
}
+ /**
+ * Loads template mapping for Front-End
+ *
+ */
function LoadStructureTemplateMapping()
{
- // get template mapping
- $sql = 'SELECT Data
- FROM ' . TABLE_PREFIX . 'Cache
- WHERE VarName = "template_mapping"';
- $template_mapping = $this->Conn->GetOne($sql);
+ if (!$this->isAdmin) {
+ $category_helper =& $this->Application->recallObject('CategoryHelper');
+ /* @var $category_helper CategoryHelper */
- if (!$this->isAdmin && $template_mapping) {
- // template mappings only for Front-End
- $this->structureTemplateMapping = unserialize($template_mapping);
+ $this->structureTemplateMapping = $category_helper->getTemplateMapping();
}
}
@@ -2099,14 +2228,15 @@
//something changed
$update = $update || $this->Phrases->NeedsCacheUpdate();
$update = $update || (count($this->ConfigCacheIds) && $this->ConfigCacheIds != $this->Caches['ConfigVariables']);
+
if ($update) {
$cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->isAdmin;
$query = sprintf("REPLACE %s (PhraseList, CacheDate, Template, ConfigVariables)
VALUES (%s, %s, %s, %s)",
TABLE_PREFIX.'PhraseCache',
- $this->Conn->Qstr(join(',', $this->Phrases->Ids)),
+ $this->Conn->qstr(join(',', $this->Phrases->Ids)),
adodb_mktime(),
- $this->Conn->Qstr(md5($cache_key)),
+ $this->Conn->qstr(md5($cache_key)),
$this->Conn->qstr(implode(',', array_unique($this->ConfigCacheIds))));
$this->Conn->Query($query);
}
@@ -2115,9 +2245,10 @@
function InitConfig()
{
if (isset($this->Caches['ConfigVariables']) && count($this->Caches['ConfigVariables']) > 0) {
- $this->ConfigHash = array_merge($this->ConfigHash, $this->Conn->GetCol(
- 'SELECT VariableValue, VariableName FROM '.TABLE_PREFIX.'ConfigurationValues
- WHERE VariableId IN ('.implode(',', $this->Caches['ConfigVariables']).')', 'VariableName'));
+ $sql = 'SELECT VariableValue, VariableName
+ FROM ' . TABLE_PREFIX . 'ConfigurationValues
+ WHERE VariableId IN (' . implode(',', $this->Caches['ConfigVariables']) . ')';
+ $this->ConfigHash = array_merge($this->ConfigHash, $this->Conn->GetCol($sql, 'VariableName'));
}
}
@@ -2988,10 +3119,8 @@
*/
function getTreeIndex($category_id)
{
- $category_template = $this->getFilename('c', $category_id); // to rebuild "category_tree" cache
+ $tree_index = $this->getCategoryCache($category_id, 'category_tree');
- $tree_index = $this->getCache('category_tree', $category_id);
-
if ($tree_index) {
$ret = Array ();
list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);