HTTPQuery = $this->Application->recallObject('HTTPQuery'); // domain based primary language $this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId'); if (!$this->primaryLanguageId) { // when domain-based language not found -> use site-wide language $this->primaryLanguageId = $this->Application->GetDefaultLanguageId(); } // domain based primary theme $this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId'); if (!$this->primaryThemeId) { // when domain-based theme not found -> use site-wide theme $this->primaryThemeId = $this->Application->GetDefaultThemeId(true); } $this->_initRewriteListeners(); } /** * Sets module prefix. * * @param string $prefix Unit config prefix. * * @return void */ public function setModulePrefix($prefix) { $this->modulePrefix = $prefix; } /** * Parses url * * @return void */ public function parseRewriteURL() { $url = $this->Application->GetVar('_mod_rw_url_'); if ( $url ) { $this->_redirectToDefaultUrlEnding($url); $url = $this->_removeUrlEnding($url); } $cached = $this->_getCachedUrl($url); if ( $cached !== false ) { $vars = $cached['vars']; $passed = $cached['passed']; } else { $vars = $this->parse($url); $passed = $vars['pass']; // also used in bottom of this method unset($vars['pass']); if ( !$this->_partsToParse ) { // don't cache 404 Not Found $this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed)); } if ( $this->Application->GetVarDirect('t', 'Post') ) { // template from POST overrides template from URL. $vars['t'] = $this->Application->GetVarDirect('t', 'Post'); if ( isset($vars['is_virtual']) && $vars['is_virtual'] ) { $vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS) } } unset($vars['is_virtual']); } foreach ($vars as $name => $value) { $this->HTTPQuery->Set($name, $value); } $this->_initAll(); // also will use parsed language to load phrases from it $this->HTTPQuery->finalizeParsing($passed); } /** * Detects url ending of given url * * @param string $url * @return string * @access protected */ protected function _findUrlEnding($url) { if ( !$url ) { return ''; } foreach ($this->_urlEndings as $url_ending) { if ( mb_substr($url, mb_strlen($url) - mb_strlen($url_ending)) == $url_ending ) { return $url_ending; } } return ''; } /** * Removes url ending from url * * @param string $url * @return string * @access protected */ protected function _removeUrlEnding($url) { $url_ending = $this->_findUrlEnding($url); if ( !$url_ending ) { return $url; } return mb_substr($url, 0, mb_strlen($url) - mb_strlen($url_ending)); } /** * Redirects user to page with default url ending, where needed * * @param string $url * @return void * @access protected */ protected function _redirectToDefaultUrlEnding($url) { $default_ending = $this->Application->ConfigValue('ModRewriteUrlEnding'); if ( $this->_findUrlEnding($url) == $default_ending || !$this->Application->ConfigValue('ForceModRewriteUrlEnding') ) { return; } // user manually typed url with different url ending -> redirect to same url with default url ending $target_url = $this->Application->BaseURL() . $this->_removeUrlEnding($url) . $default_ending; trigger_error('Mod-rewrite url "' . $_SERVER['REQUEST_URI'] . '" without "' . $default_ending . '" line ending used', E_USER_NOTICE); $this->Application->Redirect('external:' . $target_url, Array ('response_code' => 301)); } /** * Returns url parsing result from cache or false, when not yet parsed * * @param $url * @return Array|bool * @access protected */ protected function _getCachedUrl($url) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return false; } $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'CachedUrls WHERE Hash = ' . kUtil::crc32($url) . ' AND DomainId = ' . (int)$this->Application->siteDomainField('DomainId'); $data = $this->Conn->GetRow($sql); if ( $data ) { $lifetime = (int)$data['LifeTime']; // in seconds if ( ($lifetime > 0) && ($data['Cached'] + $lifetime < TIMENOW) ) { // delete expired $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls WHERE UrlId = ' . $data['UrlId']; $this->Conn->Query($sql); return false; } return unserialize($data['ParsedVars']); } return false; } /** * Caches url * * @param string $url * @param Array $data * @return void * @access protected */ protected function _setCachedUrl($url, $data) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return; } $vars = $data['vars']; $passed = $data['passed']; sort($passed); // get expiration if ( $vars['m_cat_id'] > 0 ) { $sql = 'SELECT PageExpiration FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . $vars['m_cat_id']; $expiration = $this->Conn->GetOne($sql); } // get prefixes $prefixes = Array (); $m_index = array_search('m', $passed); if ( $m_index !== false ) { unset($passed[$m_index]); if ( $vars['m_cat_id'] > 0 ) { $prefixes[] = 'c:' . $vars['m_cat_id']; } $prefixes[] = 'lang:' . $vars['m_lang']; $prefixes[] = 'theme:' . $vars['m_theme']; } foreach ($passed as $prefix) { if ( array_key_exists($prefix . '_id', $vars) && is_numeric($vars[$prefix . '_id']) ) { $prefixes[] = $prefix . ':' . $vars[$prefix . '_id']; } else { $prefixes[] = $prefix; } } $fields_hash = Array ( 'Url' => $url, 'Hash' => kUtil::crc32($url), 'DomainId' => (int)$this->Application->siteDomainField('DomainId'), 'Prefixes' => $prefixes ? '|' . implode('|', $prefixes) . '|' : '', 'ParsedVars' => serialize($data), 'Cached' => adodb_mktime(), 'LifeTime' => isset($expiration) && is_numeric($expiration) ? $expiration : -1 ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CachedUrls'); } /** * Loads all registered rewrite listeners, so they could be quickly accessed later * * @access protected */ protected function _initRewriteListeners() { static $init_done = false; if ($init_done || count($this->Application->RewriteListeners) == 0) { // not initialized OR mod-rewrite url with missing config cache return ; } foreach ($this->Application->RewriteListeners as $prefix => $listener_data) { foreach ($listener_data['listener'] as $index => $rewrite_listener) { list ($listener_prefix, $listener_method) = explode(':', $rewrite_listener); // don't use temp variable, since it will swap objects in Factory in PHP5 $this->rewriteListeners[$prefix][$index] = Array (); $this->rewriteListeners[$prefix][$index][0] = $this->Application->recallObject($listener_prefix); $this->rewriteListeners[$prefix][$index][1] = $listener_method; } } define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding')); $init_done = true; } /** * Parses given string into a set of variables (url in this case) * * @param string $string * @param string $pass_name * @return Array * @access public */ public function parse($string, $pass_name = 'pass') { // external url (could be back this website as well) if ( preg_match('/external:(.*)/', $string, $regs) ) { $string = $regs[1]; } $vars = Array (); $url_components = parse_url($string); if ( isset($url_components['query']) ) { parse_str(html_entity_decode($url_components['query']), $url_params); if ( isset($url_params[ENV_VAR_NAME]) ) { $url_params = array_merge($url_params, $this->manager->plain->parse($url_params[ENV_VAR_NAME], $pass_name)); unset($url_params[ENV_VAR_NAME]); } $vars = array_merge($vars, $url_params); } $this->_fixPass($vars, $pass_name); if ( isset($url_components['path']) ) { if ( BASE_PATH ) { $string = preg_replace('/^' . preg_quote(BASE_PATH, '/') . '/', '', $url_components['path'], 1); } else { $string = $url_components['path']; } $string = $this->_removeUrlEnding(trim($string, '/')); } else { $string = ''; } $url_parts = $string ? explode('/', mb_strtolower($string)) : Array (); $this->setModulePrefix(false); $this->_partsToParse = $url_parts; if ( ($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts ) { $this->_setDefaultValues($vars); } if ( !$url_parts ) { $this->_initAll(); $vars['t'] = $this->Application->UrlManager->getTemplateName(); return $vars; } $this->_parseLanguage($url_parts, $vars); $this->_parseTheme($url_parts, $vars); // http://site-url///[_]/