1: <?php
2:
3: /**
4: * This file contains the uri class.
5: *
6: * @package Core
7: * @subpackage Frontend_URI
8: * @author Murat Purc
9: * @copyright four for business AG <www.4fb.de>
10: * @license http://www.contenido.org/license/LIZENZ.txt
11: * @link http://www.4fb.de
12: * @link http://www.contenido.org
13: */
14:
15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
16:
17: /**
18: * Frontend URL creation. Works as a wrapper of an UriBuilder instance.
19: *
20: * @package Core
21: * @subpackage Frontend_URI
22: */
23: class cUri {
24:
25: /**
26: * Self instance.
27: *
28: * @var cUri
29: */
30: static private $_instance;
31:
32: /**
33: * UriBuilder instance.
34: *
35: * @var cUriBuilder
36: */
37: private $_oUriBuilder;
38:
39: /**
40: * UriBuilder name.
41: *
42: * @var string
43: */
44: private $_sUriBuilderName;
45:
46: /**
47: * Constructor to create an instance of this class.
48: *
49: * Is not callable from outside.
50: *
51: * Gets the UriBuilder configuration and creates an UriBuilder
52: * instance.
53: *
54: * @throws cException
55: */
56: private function __construct() {
57: $this->_sUriBuilderName = cUriBuilderConfig::getUriBuilderName();
58: $this->_oUriBuilder = cUriBuilderFactory::getUriBuilder($this->_sUriBuilderName);
59: }
60:
61: /**
62: * Returns self instance.
63: *
64: * @return cUri
65: */
66: public static function getInstance() {
67: if (self::$_instance == NULL) {
68: self::$_instance = new self();
69: }
70: return self::$_instance;
71: }
72:
73: /**
74: * Creates a URL to frontend page.
75: *
76: * @param mixed $param
77: * Either url or assoziative array containing parameter:
78: * - url: front_content.php?idcat=12&lang=1
79: * - params: array('idcat' => 12, 'lang' => 1)
80: * Required values depend on used UriBuilder, but a must have is 'lang'.
81: * @param bool $bUseAbsolutePath [optional]
82: * Flag to create absolute Urls
83: * @param array $aConfig [optional]
84: * If not set, cUriBuilderConfig::getConfig() will be used by the UriBuilder
85: * @throws cInvalidArgumentException
86: * if the given params do not contain the lang
87: * @return string
88: * The Url build by cUriBuilder
89: */
90: public function build($param, $bUseAbsolutePath = false, array $aConfig = array()) {
91: if (!is_array($param)) {
92: $arr = $this->parse($param);
93: $param = $arr['params'];
94: }
95:
96: // fallback for urls to homepage (/ or front_content.php)
97: if (count($param) == 0 || (!isset($param['idart']) && !isset($param['idartlang']) &&
98: !isset($param['idcat']) && !isset($param['idcatlang']) && !isset($param['idcatart']))) {
99: $param['idcat'] = getEffectiveSetting('navigation', 'idcat-home', 1);
100: }
101:
102: // execute preprocess hook
103: $aHookParams = array(
104: 'param' => $param, 'bUseAbsolutePath' => $bUseAbsolutePath, 'aConfig' => $aConfig
105: );
106: $aResult = cApiCecHook::executeAndReturn('Contenido.Frontend.PreprocessUrlBuilding', $aHookParams);
107: if ($aResult) {
108: $param = (isset($aResult['param'])) ? $aResult['param'] : '';
109: if (isset($aResult['bUseAbsolutePath'])) {
110: $bUseAbsolutePath = (bool) $aResult['bUseAbsolutePath'];
111: }
112: if (isset($aResult['aConfig']) && is_array($aResult['aConfig'])) {
113: $aConfig = $aResult['aConfig'];
114: }
115: }
116:
117: if ($this->_sUriBuilderName == 'custom_path' && !isset($aParams['level'])) {
118: // downwards compatibility to cUriBuilderCustomPath
119: $aParams['level'] = '1';
120: }
121:
122: if (!isset($param['lang'])) {
123: // another downwards compatibility to cUriBuilderCustomPath
124: throw new cInvalidArgumentException('$param[lang] must be set!');
125: }
126:
127: if ($this->_sUriBuilderName == 'custom_path' && count($param) <= 3) {
128: // third downwards compatibility
129: $param['_c_p_'] = '1';
130: }
131:
132: // CON-2712
133: if ($bUseAbsolutePath === true) {
134: $this->_oUriBuilder->setHttpBasePath(cRegistry::getFrontendUrl());
135: }
136:
137: $this->_oUriBuilder->buildUrl($param, $bUseAbsolutePath, $aConfig);
138:
139: $url = $this->_oUriBuilder->getUrl();
140:
141: // execute postprocess hook
142: $result = cApiCecHook::executeAndReturn('Contenido.Frontend.PostprocessUrlBuilding', $url);
143: if ($result) {
144: $url = (string) $result;
145: }
146:
147: return $url;
148: }
149:
150: /**
151: * Creates a URL used to redirect to frontend page.
152: *
153: * @param mixed $param
154: * Either url or assoziative array containing parameter:
155: * - url: front_content.php?idcat=12&lang=1
156: * - params: array('idcat' => 12, 'lang' => 1)
157: * Required values depend on used UriBuilder, but a must have is 'lang'.
158: * @param array $aConfig [optional]
159: * If not set, cUriBuilderConfig::getConfig() will be used by the UriBuilder.
160: *
161: * @return string
162: * The redirect Url build by cUriBuilder.
163: * @throws cInvalidArgumentException
164: */
165: public function buildRedirect($param, array $aConfig = array()) {
166: $url = $this->build($param, true, $aConfig);
167: return str_replace('&', '&', $url);
168: }
169:
170: /**
171: * Splits passed url into its components.
172: *
173: * @param string $sUrl
174: * The Url to strip down.
175: * @return array
176: * Assoziative array created by using parse_url()
177: * having the key 'params' which includes the parameter value pairs.
178: */
179: public function parse($sUrl) {
180: $aUrl = @parse_url($sUrl);
181:
182: if (isset($aUrl['query'])) {
183: $aUrl['query'] = str_replace('&', '&', $aUrl['query']);
184: parse_str($aUrl['query'], $aUrl['params']);
185: }
186:
187: if (!isset($aUrl['params']) || !is_array($aUrl['params'])) {
188: $aUrl['params'] = array();
189: }
190:
191: return $aUrl;
192: }
193:
194: /**
195: * Composes a url using passed components array.
196: *
197: * @param array $aComponents
198: * Assoziative array created by parse_url()
199: * @return string
200: * The composed Url
201: */
202: public function composeByComponents(array $aComponents) {
203: $sUrl = (isset($aComponents['scheme']) ? $aComponents['scheme'] . '://' : '') .
204: (isset($aComponents['user']) ? $aComponents['user'] . ':' : '') .
205: (isset($aComponents['pass']) ? $aComponents['pass'] . '@' : '') .
206: (isset($aComponents['host']) ? $aComponents['host'] : '') .
207: (isset($aComponents['port']) ? ':' . $aComponents['port'] : '') .
208: (isset($aComponents['path']) ? $aComponents['path'] : '') .
209: (isset($aComponents['query']) ? '?' . $aComponents['query'] : '') .
210: (isset($aComponents['fragment']) ? '#' . $aComponents['fragment'] : '');
211: return $sUrl;
212: }
213:
214: /**
215: * Checks, if passed url is an external url while performing
216: * hostname check.
217: *
218: * @param string $sUrl
219: * Url to check.
220: * @return bool
221: * True if url is a external url, otherwise false.
222: */
223: public function isExternalUrl($sUrl) {
224: $aComponents = $this->parse($sUrl);
225: if (!isset($aComponents['host'])) {
226: return false;
227: }
228: if (!$path = $this->_oUriBuilder->getHttpBasePath()) {
229: return false;
230: }
231:
232: $aComponents2 = $this->parse($path);
233: if (!isset($aComponents2['host'])) {
234: return false;
235: }
236:
237: return cString::toLowerCase($aComponents['host']) !== cString::toLowerCase($aComponents2['host']);
238: }
239:
240: /**
241: * Checks, if passed url is an identifiable internal url.
242: *
243: * Following urls will be identified as a internal url:
244: *
245: * - "/", "/?idart=123", "/?idcat=123", ...
246: * - "front_content.php", "front_content.php?idart=123", "front_content.php?idcat=123", ...
247: * - "/front_content.php", "/front_content.php?idart=123", "/front_content.php?idcat=123", ...
248: * - The path component of an client HTML base path: e.g. "/cms/", "/cms/?idart=123", "/cms/?idcat=123"
249: * - Also possible: "/cms/front_content.php", "/cms/front_content.php?idart=123", "/cms/front_content.php?idcat=123"
250: *
251: * All of them prefixed with protocol and client host (e.g. http://host/) will also be identified
252: * as a internal Url.
253: *
254: * Other Urls, even internal Urls like /unknown/path/to/some/page.html
255: * will not be identified as internal url event if they are real
256: * working clean URLs.
257: *
258: * @param string $sUrl
259: * Url to check.
260: * @return bool
261: * True if url is identifiable internal url, otherwise false.
262: */
263: public function isIdentifiableFrontContentUrl($sUrl) {
264: if ($this->isExternalUrl($sUrl)) {
265: // detect a external url, return false
266: return false;
267: }
268:
269: $aComponents = $this->parse($sUrl);
270: if (!isset($aComponents['path']) || $aComponents['path'] == '') {
271: return false;
272: }
273:
274: $clientPath = '';
275: if ($httpBasePath = $this->_oUriBuilder->getHttpBasePath()) {
276: $aComponents2 = $this->parse($httpBasePath);
277: if (isset($aComponents2['path'])) {
278: $clientPath = $aComponents2['path'];
279: }
280: }
281:
282: // Use pathinfo to get the path part (dirname) of the url
283: $pathinfo = pathinfo($aComponents['path']);
284: $baseName = $pathinfo['basename'];
285: $path = $pathinfo['dirname'];
286: $path = str_replace('\\', '/', $path);
287: if ($path == '.') {
288: $path = '';
289: }
290:
291: // Remove leading/ending slashes
292: $path = trim($path, '/');
293: $clientPath = trim($clientPath, '/');
294:
295: if (($path == '' && ($baseName == 'front_content.php' || $baseName == ''))) {
296: return true;
297: } elseif (($path == $clientPath && ($baseName == 'front_content.php' || $baseName == ''))) {
298: return true;
299: } elseif ($path == '' && $baseName !== 'front_content.php' && $baseName == $clientPath) {
300: // If url is e.g. "/cms/"
301: return true;
302: } else {
303: return false;
304: }
305: }
306:
307: /**
308: * Returns UriBuilder instance.
309: *
310: * @return cUriBuilder
311: */
312: public function getUriBuilder() {
313: return $this->_oUriBuilder;
314: }
315:
316: }
317: