[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Router/ -> SiteRouter.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer