* @copyright (c) 2022, conlite.org * @license http://www.gnu.de/documents/gpl.en.html GPL v3 (english version) * @license http://www.gnu.de/documents/gpl.de.html GPL v3 (deutsche Version) * @link http://www.conlite.org ConLite.org * * based on former AMR class by Murat Purc for CONTENIDO 4.8.x */ if (!defined('CON_FRAMEWORK')) { die('Illegal call'); } class ModRewriteUrlStack { /** * Self instance * * @var ModRewriteUrlStack */ private static $_oInstance; /** * Database object * * @var DB_Contenido */ private $_oDb; /** * Array for urls * * @var array */ private $_aUrls = array(); /** * Url stack array * * @var array */ private $_aStack = array(); /** * CONTENIDO related parameter array * * @var array */ private $_aConParams = array( 'idcat' => 1, 'idart' => 1, 'lang' => 1, 'idcatlang' => 1, 'idcatart' => 1, 'idartlang' => 1 ); /** * Database tables array * * @var array */ private $_aTab; /** * Language id * * @var int */ private $_idLang; /** * Constructor, sets some properties. */ private function __construct() { $this->_oDb = cRegistry::getDb(); $this->_aTab = cRegistry::getConfigValue('tab'); $this->_idLang = cRegistry::getLanguageId(); } /** * Returns a instance of ModRewriteUrlStack (singleton implementation) * * @return ModRewriteUrlStack */ public static function getInstance() { if (self::$_oInstance == null) { self::$_oInstance = new ModRewriteUrlStack(); } return self::$_oInstance; } /** * Adds an url to the stack * * @param string Url, like front_content.php?idcat=123... */ public function add($url) { $url = ModRewrite::urlPreClean($url); if (isset($this->_aUrls[$url])) { return; } $aUrl = $this->_extractUrl($url); // cleanup parameter foreach ($aUrl['params'] as $p => $v) { if (!isset($this->_aConParams[$p])) { unset($aUrl['params'][$p]); } else { $aUrl['params'][$p] = (int) $v; } } // add language id, if not available if ((int) mr_arrayValue($aUrl['params'], 'lang') == 0) { $aUrl['params']['lang'] = $this->_idLang; } $sStackId = $this->_makeStackId($aUrl['params']); $this->_aUrls[$url] = $sStackId; $this->_aStack[$sStackId] = array('params' => $aUrl['params']); } /** * Returns the pretty urlparts (only category path an article name) of the * desired url. * * @param string Url, like front_content.php?idcat=123... * @return array Assoziative array like * * $arr['urlpath'] * $arr['urlname'] * */ public function getPrettyUrlParts($url) { $url = ModRewrite::urlPreClean($url); if (!isset($this->_aUrls[$url])) { $this->add($url); } $sStackId = $this->_aUrls[$url]; if (!isset($this->_aStack[$sStackId]['urlpath'])) { $this->_chunkSetPrettyUrlParts($sStackId); } $aPretty = array( 'urlpath' => (empty($this->_aStack[$sStackId]['urlpath']))?'':$this->_aStack[$sStackId]['urlpath'], 'urlname' => (empty($this->_aStack[$sStackId]['urlname']))?'':$this->_aStack[$sStackId]['urlname'] ); return $aPretty; } /** * Extracts passed url using parse_urla and adds also the 'params' array to it * * @param string Url, like front_content.php?idcat=123... * @return array Components containing result of parse_url with additional * 'params' array */ private function _extractUrl($url) { $aUrl = @parse_url($url); if (isset($aUrl['query'])) { $aUrl['query'] = str_replace('&', '&', $aUrl['query']); parse_str($aUrl['query'], $aUrl['params']); } if (empty($aUrl['params']) || !is_array($aUrl['params'])) { $aUrl['params'] = array(); } return $aUrl; } /** * Extracts article or category related parameter from passed params array * and generates an identifier. * * @param array $aParams Parameter array * @return string Composed stack id */ private function _makeStackId(array $aParams) { # idcatart if ((int) mr_arrayValue($aParams, 'idart') > 0) { $sStackId = 'idart_' . $aParams['idart'] . '_lang_' . $aParams['lang']; } elseif ((int) mr_arrayValue($aParams, 'idartlang') > 0) { $sStackId = 'idartlang_' . $aParams['idartlang']; } elseif ((int) mr_arrayValue($aParams, 'idcatart') > 0) { $sStackId = 'idcatart_' . $aParams['idcatart'] . '_lang_' . $aParams['lang']; } elseif ((int) mr_arrayValue($aParams, 'idcat') > 0) { $sStackId = 'idcat_' . $aParams['idcat'] . '_lang_' . $aParams['lang']; } elseif ((int) mr_arrayValue($aParams, 'idcatlang') > 0) { $sStackId = 'idcatlang_' . $aParams['idcatlang']; } else { $sStackId = 'lang_' . $aParams['lang']; } return $sStackId; } /** * Main function to get the urlparts of urls. * * Composes the query by looping thru stored but non processed urls, executes * the query and adds the (urlpath and urlname) result to the stack. * * @param type $sStackId * @return type */ private function _chunkSetPrettyUrlParts($sStackId) { if (!isset($this->_aStack[$sStackId]) || isset($this->_aStack[$sStackId]['urlpath'])) { return; } $aStack = []; $aStack[$sStackId] = $this->_aStack[$sStackId]; // now, it's time to compose the where clause of the query $sWhere = ''; $aP = $this->_aStack[$sStackId]['params']; if ((int) mr_arrayValue($aP, 'idart') > 0) { $sWhere .= '(al.idart = ' . $aP['idart'] . ' AND al.idlang = ' . $aP['lang'] . ') OR '; } elseif ((int) mr_arrayValue($aP, 'idartlang') > 0) { $sWhere .= '(al.idartlang = ' . $aP['idartlang'] . ') OR '; } elseif ((int) mr_arrayValue($aP, 'idcat') > 0) { $sWhere .= '(cl.idcat = ' . $aP['idcat'] . ' AND cl.idlang = ' . $aP['lang'] . ' AND cl.startidartlang = al.idartlang) OR '; } elseif ((int) mr_arrayValue($aP, 'idcatart') > 0) { $sWhere .= '(ca.idcatart = ' . $aP['idcatart'] . ' AND ca.idart = al.idart AND al.idlang = ' . $aP['lang'] . ') OR '; } elseif ((int) mr_arrayValue($aP, 'idcatlang') > 0) { $sWhere .= '(cl.idcatlang = ' . $aP['idcatlang'] . ' AND cl.startidartlang = al.idartlang) OR '; } if ($sWhere == '') { return; } $sWhere = cString::getPartOfString($sWhere, 0, -4); $sWhere = str_replace(' OR ', " OR \n", $sWhere); // compose query and execute it $sql = <<_aTab['art_lang']} AS al, {$this->_aTab['cat_lang']} AS cl, {$this->_aTab['cat_art']} AS ca WHERE al.idart = ca.idart AND ca.idcat = cl.idcat AND al.idlang = cl.idlang AND ( $sWhere ) SQL; ModRewriteDebugger::add($sql, 'ModRewriteUrlStack->_chunkSetPrettyUrlParts() $sql'); $aNewStack = []; // create array of fields, which are to reduce step by step from record set below $aFields = array('', 'idart', 'idartlang', 'idcatart', 'idcat'); $this->_oDb->query($sql); while ($this->_oDb->next_record()) { $aRS = $this->_oDb->Record; // loop thru fields array foreach ($aFields as $field) { if (isset($aRS[$field])) { // reduce existing field unset($aRS[$field]); } // matching stack entry found, add urlpath and urlname to the new stack $aNewStack[$sStackId]['urlpath'] = $aRS['urlpath']; $aNewStack[$sStackId]['urlname'] = $aRS['urlname']; } } ModRewriteDebugger::add($aNewStack, 'ModRewriteUrlStack->_chunkSetPrettyUrlParts() $aNewStack'); ModRewriteDebugger::add($this->_aStack, 'ModRewriteUrlStack->_chunkSetPrettyUrlParts() $this->_aStack'); $this->_aStack = array_merge($this->_aStack, $aNewStack); } }