[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Router; 11 12 use Joomla\CMS\Application\CMSApplication; 13 use Joomla\CMS\Application\SiteApplication; 14 use Joomla\CMS\Component\Router\RouterInterface; 15 use Joomla\CMS\Component\Router\RouterLegacy; 16 use Joomla\CMS\Component\Router\RouterServiceInterface; 17 use Joomla\CMS\Factory; 18 use Joomla\CMS\Menu\AbstractMenu; 19 use Joomla\CMS\Uri\Uri; 20 21 // phpcs:disable PSR1.Files.SideEffects 22 \defined('JPATH_PLATFORM') or die; 23 // phpcs:enable PSR1.Files.SideEffects 24 25 /** 26 * Class to create and parse routes for the site application 27 * 28 * @since 1.5 29 */ 30 class SiteRouter extends Router 31 { 32 /** 33 * Component-router objects 34 * 35 * @var array 36 * 37 * @since 3.3 38 */ 39 protected $componentRouters = []; 40 41 /** 42 * @var CMSApplication 43 * 44 * @since 3.4 45 */ 46 protected $app; 47 48 /** 49 * Current Menu-Object 50 * 51 * @var AbstractMenu 52 * 53 * @since 3.4 54 */ 55 protected $menu; 56 57 /** 58 * Class constructor 59 * 60 * @param CMSApplication $app Application Object 61 * @param AbstractMenu $menu Menu object 62 * 63 * @since 3.4 64 */ 65 public function __construct(CMSApplication $app = null, AbstractMenu $menu = null) 66 { 67 $this->app = $app ?: Factory::getContainer()->get(SiteApplication::class); 68 $this->menu = $menu ?: $this->app->getMenu(); 69 70 // Add core rules 71 if ($this->app->get('force_ssl') === 2) { 72 $this->attachParseRule(array($this, 'parseCheckSSL'), self::PROCESS_BEFORE); 73 } 74 75 $this->attachParseRule(array($this, 'parseInit'), self::PROCESS_BEFORE); 76 $this->attachBuildRule(array($this, 'buildInit'), self::PROCESS_BEFORE); 77 $this->attachBuildRule(array($this, 'buildComponentPreprocess'), self::PROCESS_BEFORE); 78 79 if ($this->app->get('sef', 1)) { 80 if ($this->app->get('sef_suffix')) { 81 $this->attachParseRule(array($this, 'parseFormat'), self::PROCESS_BEFORE); 82 $this->attachBuildRule(array($this, 'buildFormat'), self::PROCESS_AFTER); 83 } 84 85 $this->attachParseRule(array($this, 'parseSefRoute'), self::PROCESS_DURING); 86 $this->attachBuildRule(array($this, 'buildSefRoute'), self::PROCESS_DURING); 87 $this->attachParseRule(array($this, 'parsePaginationData'), self::PROCESS_AFTER); 88 $this->attachBuildRule(array($this, 'buildPaginationData'), self::PROCESS_AFTER); 89 90 if ($this->app->get('sef_rewrite')) { 91 $this->attachBuildRule(array($this, 'buildRewrite'), self::PROCESS_AFTER); 92 } 93 } 94 95 $this->attachParseRule(array($this, 'parseRawRoute'), self::PROCESS_DURING); 96 $this->attachBuildRule(array($this, 'buildBase'), self::PROCESS_AFTER); 97 } 98 99 /** 100 * Force to SSL 101 * 102 * @param Router &$router Router object 103 * @param Uri &$uri URI object to process 104 * 105 * @return void 106 * 107 * @since 4.0.0 108 */ 109 public function parseCheckSSL(&$router, &$uri) 110 { 111 if (strtolower($uri->getScheme()) !== 'https') { 112 // Forward to https 113 $uri->setScheme('https'); 114 $this->app->redirect((string) $uri, 301); 115 } 116 } 117 118 /** 119 * Do some initial cleanup before parsing the URL 120 * 121 * @param SiteRouter &$router Router object 122 * @param Uri &$uri URI object to process 123 * 124 * @return void 125 * 126 * @since 4.0.0 127 */ 128 public function parseInit(&$router, &$uri) 129 { 130 // Get the path 131 // Decode URL to convert percent-encoding to unicode so that strings match when routing. 132 $path = urldecode($uri->getPath()); 133 134 /** 135 * In some environments (e.g. CLI we can't form a valid base URL). In this case we catch the exception thrown 136 * by URI and set an empty base URI for further work. 137 * @todo: This should probably be handled better 138 */ 139 try { 140 $baseUri = Uri::base(true); 141 } catch (\RuntimeException $e) { 142 $baseUri = ''; 143 } 144 145 // Remove the base URI path. 146 $path = substr_replace($path, '', 0, \strlen($baseUri)); 147 148 // Check to see if a request to a specific entry point has been made. 149 if (preg_match("#.*?\.php#u", $path, $matches)) { 150 // Get the current entry point path relative to the site path. 151 $scriptPath = realpath($_SERVER['SCRIPT_FILENAME'] ?: str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED'])); 152 $relativeScriptPath = str_replace('\\', '/', str_replace(JPATH_SITE, '', $scriptPath)); 153 154 // If a php file has been found in the request path, check to see if it is a valid file. 155 // Also verify that it represents the same file from the server variable for entry script. 156 if (is_file(JPATH_SITE . $matches[0]) && ($matches[0] === $relativeScriptPath)) { 157 // Remove the entry point segments from the request path for proper routing. 158 $path = str_replace($matches[0], '', $path); 159 } 160 } 161 162 // Set the route 163 $uri->setPath(trim($path, '/')); 164 } 165 166 /** 167 * Parse the format of the request 168 * 169 * @param SiteRouter &$router Router object 170 * @param Uri &$uri URI object to process 171 * 172 * @return void 173 * 174 * @since 4.0.0 175 */ 176 public function parseFormat(&$router, &$uri) 177 { 178 $route = $uri->getPath(); 179 180 // Identify format 181 if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $suffix = pathinfo($route, PATHINFO_EXTENSION)) { 182 $uri->setVar('format', $suffix); 183 $route = str_replace('.' . $suffix, '', $route); 184 $uri->setPath($route); 185 } 186 } 187 188 /** 189 * Convert a sef route to an internal URI 190 * 191 * @param SiteRouter &$router Router object 192 * @param Uri &$uri URI object to process 193 * 194 * @return void 195 * 196 * @since 4.0.0 197 */ 198 public function parseSefRoute(&$router, &$uri) 199 { 200 $route = $uri->getPath(); 201 202 // If the URL is empty, we handle this in the non-SEF parse URL 203 if (empty($route)) { 204 return; 205 } 206 207 // Parse the application route 208 $segments = explode('/', $route); 209 210 if (\count($segments) > 1 && $segments[0] === 'component') { 211 $uri->setVar('option', 'com_' . $segments[1]); 212 $uri->setVar('Itemid', null); 213 $route = implode('/', \array_slice($segments, 2)); 214 } else { 215 // Get menu items. 216 $items = $this->menu->getItems(['parent_id', 'access'], [1, null]); 217 $lang_tag = $this->app->getLanguage()->getTag(); 218 $found = null; 219 220 foreach ($segments as $segment) { 221 $matched = false; 222 223 foreach ($items as $item) { 224 if ( 225 $item->alias == $segment 226 && (!$this->app->getLanguageFilter() 227 || ($item->language === '*' 228 || $item->language === $lang_tag)) 229 ) { 230 $found = $item; 231 $matched = true; 232 $items = $item->getChildren(); 233 break; 234 } 235 } 236 237 if (!$matched) { 238 break; 239 } 240 } 241 242 // Menu links are not valid URLs. Find the first parent that isn't a menulink 243 if ($found && $found->type === 'menulink') { 244 while ($found->hasParent() && $found->type === 'menulink') { 245 $found = $found->getParent(); 246 } 247 248 if ($found->type === 'menulink') { 249 $found = null; 250 } 251 } 252 253 if (!$found) { 254 $found = $this->menu->getDefault($lang_tag); 255 } else { 256 $route = trim(substr($route, \strlen($found->route)), '/'); 257 } 258 259 if ($found) { 260 if ($found->type === 'alias') { 261 $newItem = $this->menu->getItem($found->getParams()->get('aliasoptions')); 262 263 if ($newItem) { 264 $found->query = array_merge($found->query, $newItem->query); 265 $found->component = $newItem->component; 266 } 267 } 268 269 $uri->setVar('Itemid', $found->id); 270 $uri->setVar('option', $found->component); 271 } 272 } 273 274 // Set the active menu item 275 if ($uri->getVar('Itemid')) { 276 $this->menu->setActive($uri->getVar('Itemid')); 277 } 278 279 // Parse the component route 280 if (!empty($route) && $uri->getVar('option')) { 281 $segments = explode('/', $route); 282 283 if (\count($segments)) { 284 // Handle component route 285 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $uri->getVar('option')); 286 $crouter = $this->getComponentRouter($component); 287 $uri->setQuery(array_merge($uri->getQuery(true), $crouter->parse($segments))); 288 } 289 290 $route = implode('/', $segments); 291 } 292 293 $uri->setPath($route); 294 } 295 296 /** 297 * Convert a raw route to an internal URI 298 * 299 * @param SiteRouter &$router Router object 300 * @param Uri &$uri URI object to process 301 * 302 * @return void 303 * 304 * @since 4.0.0 305 */ 306 public function parseRawRoute(&$router, &$uri) 307 { 308 if ($uri->getVar('Itemid')) { 309 $item = $this->menu->getItem($uri->getVar('Itemid')); 310 } else { 311 $item = $this->menu->getDefault($this->app->getLanguage()->getTag()); 312 } 313 314 if ($item && $item->type === 'alias') { 315 $newItem = $this->menu->getItem($item->getParams()->get('aliasoptions')); 316 317 if ($newItem) { 318 $item->query = array_merge($item->query, $newItem->query); 319 $item->component = $newItem->component; 320 } 321 } 322 323 if (\is_object($item)) { 324 // Set the active menu item 325 $this->menu->setActive($item->id); 326 327 $uri->setVar('Itemid', $item->id); 328 $uri->setQuery(array_merge($item->query, $uri->getQuery(true))); 329 } 330 } 331 332 /** 333 * Convert limits for pagination 334 * 335 * @param SiteRouter &$router Router object 336 * @param Uri &$uri URI object to process 337 * 338 * @return void 339 * 340 * @since 4.0.0 341 */ 342 public function parsePaginationData(&$router, &$uri) 343 { 344 // Process the pagination support 345 $start = $uri->getVar('start'); 346 347 if ($start !== null) { 348 $uri->setVar('limitstart', $uri->getVar('start')); 349 $uri->delVar('start'); 350 } 351 } 352 353 /** 354 * Do some initial processing for building a URL 355 * 356 * @param SiteRouter &$router Router object 357 * @param Uri &$uri URI object to process 358 * 359 * @return void 360 * 361 * @since 4.0.0 362 */ 363 public function buildInit(&$router, &$uri) 364 { 365 $itemid = $uri->getVar('Itemid'); 366 367 // If no Itemid and option given, merge in the current requests data 368 if (!$itemid && !$uri->getVar('option')) { 369 $uri->setQuery(array_merge($this->getVars(), $uri->getQuery(true))); 370 } 371 372 // If Itemid is given, but no option, set the option from the menu item 373 if ($itemid && !$uri->getVar('option')) { 374 if ($item = $this->menu->getItem($itemid)) { 375 $uri->setVar('option', $item->component); 376 } 377 } 378 } 379 380 /** 381 * Run the component preprocess method 382 * 383 * @param SiteRouter &$router Router object 384 * @param Uri &$uri URI object to process 385 * 386 * @return void 387 * 388 * @since 4.0.0 389 */ 390 public function buildComponentPreprocess(&$router, &$uri) 391 { 392 // Get the query data 393 $query = $uri->getQuery(true); 394 395 if (!isset($query['option'])) { 396 return; 397 } 398 399 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']); 400 $crouter = $this->getComponentRouter($component); 401 $query = $crouter->preprocess($query); 402 403 // Make sure any menu vars are used if no others are specified 404 if ( 405 isset($query['Itemid']) 406 && (\count($query) === 2 || (\count($query) === 3 && isset($query['lang']))) 407 ) { 408 // Get the active menu item 409 $item = $this->menu->getItem($query['Itemid']); 410 $query = array_merge($item->query, $query); 411 } 412 413 $uri->setQuery($query); 414 } 415 416 /** 417 * Build the SEF route 418 * 419 * @param SiteRouter &$router Router object 420 * @param Uri &$uri URI object to process 421 * 422 * @return void 423 * 424 * @since 4.0.0 425 */ 426 public function buildSefRoute(&$router, &$uri) 427 { 428 // Get the query data 429 $query = $uri->getQuery(true); 430 431 if (!isset($query['option'])) { 432 return; 433 } 434 435 // Get Menu Item 436 $item = empty($query['Itemid']) ? null : $this->menu->getItem($query['Itemid']); 437 438 // Build the component route 439 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']); 440 $crouter = $this->getComponentRouter($component); 441 $parts = $crouter->build($query); 442 $tmp = trim(implode('/', $parts)); 443 444 // Build the application route 445 if ($item !== null && $query['option'] === $item->component) { 446 if (!$item->home) { 447 $tmp = $tmp ? $item->route . '/' . $tmp : $item->route; 448 } 449 450 unset($query['Itemid']); 451 } else { 452 $tmp = 'component/' . substr($query['option'], 4) . '/' . $tmp; 453 } 454 455 // Get the route 456 if ($tmp) { 457 $uri->setPath($uri->getPath() . '/' . $tmp); 458 } 459 460 // Unset unneeded query information 461 unset($query['option']); 462 463 // Set query again in the URI 464 $uri->setQuery($query); 465 } 466 467 /** 468 * Convert limits for pagination 469 * 470 * @param SiteRouter &$router Router object 471 * @param Uri &$uri URI object to process 472 * 473 * @return void 474 * 475 * @since 4.0.0 476 */ 477 public function buildPaginationData(&$router, &$uri) 478 { 479 $limitstart = $uri->getVar('limitstart'); 480 481 if ($limitstart !== null) { 482 $uri->setVar('start', (int) $uri->getVar('limitstart')); 483 $uri->delVar('limitstart'); 484 } 485 } 486 487 /** 488 * Build the format of the request 489 * 490 * @param SiteRouter &$router Router object 491 * @param Uri &$uri URI object to process 492 * 493 * @return void 494 * 495 * @since 4.0.0 496 */ 497 public function buildFormat(&$router, &$uri) 498 { 499 $route = $uri->getPath(); 500 501 // Identify format 502 if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $format = $uri->getVar('format', 'html')) { 503 $route .= '.' . $format; 504 $uri->setPath($route); 505 $uri->delVar('format'); 506 } 507 } 508 509 /** 510 * Create a uri based on a full or partial URL string 511 * 512 * @param SiteRouter &$router Router object 513 * @param Uri &$uri URI object to process 514 * 515 * @return void 516 * 517 * @since 4.0.0 518 */ 519 public function buildRewrite(&$router, &$uri) 520 { 521 // Get the path data 522 $route = $uri->getPath(); 523 524 // Transform the route 525 if ($route === 'index.php') { 526 $route = ''; 527 } else { 528 $route = str_replace('index.php/', '', $route); 529 } 530 531 $uri->setPath($route); 532 } 533 534 /** 535 * Add the basepath to the URI 536 * 537 * @param SiteRouter &$router Router object 538 * @param Uri &$uri URI object to process 539 * 540 * @return void 541 * 542 * @since 4.0.0 543 */ 544 public function buildBase(&$router, &$uri) 545 { 546 // Add frontend basepath to the uri 547 $uri->setPath(Uri::root(true) . '/' . $uri->getPath()); 548 } 549 550 /** 551 * Get component router 552 * 553 * @param string $component Name of the component including com_ prefix 554 * 555 * @return RouterInterface Component router 556 * 557 * @since 3.3 558 */ 559 public function getComponentRouter($component) 560 { 561 if (!isset($this->componentRouters[$component])) { 562 $componentInstance = $this->app->bootComponent($component); 563 564 if ($componentInstance instanceof RouterServiceInterface) { 565 $this->componentRouters[$component] = $componentInstance->createRouter($this->app, $this->menu); 566 } 567 568 if (!isset($this->componentRouters[$component])) { 569 $this->componentRouters[$component] = new RouterLegacy(ucfirst(substr($component, 4))); 570 } 571 } 572 573 return $this->componentRouters[$component]; 574 } 575 576 /** 577 * Set a router for a component 578 * 579 * @param string $component Component name with com_ prefix 580 * @param object $router Component router 581 * 582 * @return boolean True if the router was accepted, false if not 583 * 584 * @since 3.3 585 */ 586 public function setComponentRouter($component, $router) 587 { 588 $reflection = new \ReflectionClass($router); 589 590 if (\in_array('Joomla\\CMS\\Component\\Router\\RouterInterface', $reflection->getInterfaceNames())) { 591 $this->componentRouters[$component] = $router; 592 593 return true; 594 } else { 595 return false; 596 } 597 } 598 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |