Current File : /home/pacjaorg/public_html/nsa/libraries/src/Router/SiteRouter.php |
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Router;
\defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\Router\RouterInterface;
use Joomla\CMS\Component\Router\RouterLegacy;
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Menu\AbstractMenu;
use Joomla\CMS\Uri\Uri;
/**
* Class to create and parse routes for the site application
*
* @since 1.5
*/
class SiteRouter extends Router
{
/**
* Component-router objects
*
* @var array
* @since 3.3
*/
protected $componentRouters = array();
/**
* Current Application-Object
*
* @var CMSApplication
* @since 3.4
*/
protected $app;
/**
* Current Menu-Object
*
* @var AbstractMenu
* @since 3.4
*/
protected $menu;
/**
* Class constructor
*
* @param CMSApplication $app JApplicationCms Object
* @param AbstractMenu $menu JMenu object
*
* @since 3.4
*/
public function __construct(CMSApplication $app = null, AbstractMenu $menu = null)
{
$this->app = $app ?: Factory::getContainer()->get(SiteApplication::class);
$this->menu = $menu ?: $this->app->getMenu();
// Add core rules
if ($this->app->get('force_ssl') === 2)
{
$this->attachParseRule(array($this, 'parseCheckSSL'), self::PROCESS_BEFORE);
}
$this->attachParseRule(array($this, 'parseInit'), self::PROCESS_BEFORE);
$this->attachBuildRule(array($this, 'buildInit'), self::PROCESS_BEFORE);
$this->attachBuildRule(array($this, 'buildComponentPreprocess'), self::PROCESS_BEFORE);
if ($this->app->get('sef', 1))
{
if ($this->app->get('sef_suffix'))
{
$this->attachParseRule(array($this, 'parseFormat'), self::PROCESS_BEFORE);
$this->attachBuildRule(array($this, 'buildFormat'), self::PROCESS_AFTER);
}
$this->attachParseRule(array($this, 'parseSefRoute'), self::PROCESS_DURING);
$this->attachBuildRule(array($this, 'buildSefRoute'), self::PROCESS_DURING);
$this->attachParseRule(array($this, 'parsePaginationData'), self::PROCESS_AFTER);
$this->attachBuildRule(array($this, 'buildPaginationData'), self::PROCESS_AFTER);
if ($this->app->get('sef_rewrite'))
{
$this->attachBuildRule(array($this, 'buildRewrite'), self::PROCESS_AFTER);
}
}
$this->attachParseRule(array($this, 'parseRawRoute'), self::PROCESS_DURING);
$this->attachBuildRule(array($this, 'buildBase'), self::PROCESS_AFTER);
}
/**
* Force to SSL
*
* @param Router &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parseCheckSSL(&$router, &$uri)
{
if (strtolower($uri->getScheme()) !== 'https')
{
// Forward to https
$uri->setScheme('https');
$this->app->redirect((string) $uri, 301);
}
}
/**
* Do some initial cleanup before parsing the URL
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parseInit(&$router, &$uri)
{
// Get the path
// Decode URL to convert percent-encoding to unicode so that strings match when routing.
$path = urldecode($uri->getPath());
/**
* In some environments (e.g. CLI we can't form a valid base URL). In this case we catch the exception thrown
* by URI and set an empty base URI for further work.
* TODO: This should probably be handled better
*/
try
{
$baseUri = Uri::base(true);
}
catch (\RuntimeException $e)
{
$baseUri = '';
}
// Remove the base URI path.
$path = substr_replace($path, '', 0, \strlen($baseUri));
// Check to see if a request to a specific entry point has been made.
if (preg_match("#.*?\.php#u", $path, $matches))
{
// Get the current entry point path relative to the site path.
$scriptPath = realpath($_SERVER['SCRIPT_FILENAME'] ?: str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']));
$relativeScriptPath = str_replace('\\', '/', str_replace(JPATH_SITE, '', $scriptPath));
// If a php file has been found in the request path, check to see if it is a valid file.
// Also verify that it represents the same file from the server variable for entry script.
if (is_file(JPATH_SITE . $matches[0]) && ($matches[0] === $relativeScriptPath))
{
// Remove the entry point segments from the request path for proper routing.
$path = str_replace($matches[0], '', $path);
}
}
// Set the route
$uri->setPath(trim($path, '/'));
}
/**
* Parse the format of the request
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parseFormat(&$router, &$uri)
{
$route = $uri->getPath();
// Identify format
if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $suffix = pathinfo($route, PATHINFO_EXTENSION))
{
$uri->setVar('format', $suffix);
$route = str_replace('.' . $suffix, '', $route);
$uri->setPath($route);
}
}
/**
* Convert a sef route to an internal URI
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parseSefRoute(&$router, &$uri)
{
$route = $uri->getPath();
// If the URL is empty, we handle this in the non-SEF parse URL
if (empty($route))
{
return;
}
// Parse the application route
$segments = explode('/', $route);
if (\count($segments) > 1 && $segments[0] === 'component')
{
$uri->setVar('option', 'com_' . $segments[1]);
$uri->setVar('Itemid', null);
$route = implode('/', \array_slice($segments, 2));
}
else
{
// Get menu items.
$items = $this->menu->getItems(['parent_id', 'access'], [1, null]);
$lang_tag = $this->app->getLanguage()->getTag();
$found = null;
foreach ($segments as $segment)
{
$matched = false;
foreach ($items as $item)
{
if ($item->alias == $segment
&& (!$this->app->getLanguageFilter()
|| ($item->language === '*'
|| $item->language === $lang_tag)))
{
$found = $item;
$matched = true;
$items = $item->getChildren();
break;
}
}
if (!$matched)
{
break;
}
}
// Menu links are not valid URLs. Find the first parent that isn't a menulink
if ($found && $found->type === 'menulink')
{
while ($found->hasParent() && $found->type === 'menulink')
{
$found = $found->getParent();
}
if ($found->type === 'menulink')
{
$found = null;
}
}
if (!$found)
{
$found = $this->menu->getDefault($lang_tag);
}
else
{
$route = trim(substr($route, \strlen($found->route)), '/');
}
if ($found)
{
if ($found->type === 'alias')
{
$newItem = $this->menu->getItem($found->getParams()->get('aliasoptions'));
if ($newItem)
{
$found->query = array_merge($found->query, $newItem->query);
$found->component = $newItem->component;
}
}
$uri->setVar('Itemid', $found->id);
$uri->setVar('option', $found->component);
}
}
// Set the active menu item
if ($uri->getVar('Itemid'))
{
$this->menu->setActive($uri->getVar('Itemid'));
}
// Parse the component route
if (!empty($route) && $uri->getVar('option'))
{
$segments = explode('/', $route);
if (\count($segments))
{
// Handle component route
$component = preg_replace('/[^A-Z0-9_\.-]/i', '', $uri->getVar('option'));
$crouter = $this->getComponentRouter($component);
$uri->setQuery(array_merge($uri->getQuery(true), $crouter->parse($segments)));
}
$route = implode('/', $segments);
}
$uri->setPath($route);
}
/**
* Convert a raw route to an internal URI
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parseRawRoute(&$router, &$uri)
{
if ($uri->getVar('Itemid'))
{
$item = $this->menu->getItem($uri->getVar('Itemid'));
}
else
{
$item = $this->menu->getDefault($this->app->getLanguage()->getTag());
}
if ($item && $item->type === 'alias')
{
$newItem = $this->menu->getItem($item->getParams()->get('aliasoptions'));
if ($newItem)
{
$item->query = array_merge($item->query, $newItem->query);
$item->component = $newItem->component;
}
}
if (\is_object($item))
{
// Set the active menu item
$this->menu->setActive($item->id);
$uri->setVar('Itemid', $item->id);
$uri->setQuery(array_merge($item->query, $uri->getQuery(true)));
}
}
/**
* Convert limits for pagination
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function parsePaginationData(&$router, &$uri)
{
// Process the pagination support
$start = $uri->getVar('start');
if ($start !== null)
{
$uri->setVar('limitstart', $uri->getVar('start'));
$uri->delVar('start');
}
}
/**
* Do some initial processing for building a URL
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildInit(&$router, &$uri)
{
$itemid = $uri->getVar('Itemid');
// If no Itemid and option given, merge in the current requests data
if (!$itemid && !$uri->getVar('option'))
{
$uri->setQuery(array_merge($this->getVars(), $uri->getQuery(true)));
}
// If Itemid is given, but no option, set the option from the menu item
if ($itemid && !$uri->getVar('option'))
{
if ($item = $this->menu->getItem($itemid))
{
$uri->setVar('option', $item->component);
}
}
}
/**
* Run the component preprocess method
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildComponentPreprocess(&$router, &$uri)
{
// Get the query data
$query = $uri->getQuery(true);
if (!isset($query['option']))
{
return;
}
$component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']);
$crouter = $this->getComponentRouter($component);
$query = $crouter->preprocess($query);
// Make sure any menu vars are used if no others are specified
if (isset($query['Itemid'])
&& (\count($query) === 2 || (\count($query) === 3 && isset($query['lang']))))
{
// Get the active menu item
$item = $this->menu->getItem($query['Itemid']);
$query = array_merge($item->query, $query);
}
$uri->setQuery($query);
}
/**
* Build the SEF route
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildSefRoute(&$router, &$uri)
{
// Get the query data
$query = $uri->getQuery(true);
if (!isset($query['option']))
{
return;
}
// Get Menu Item
$item = empty($query['Itemid']) ? null : $this->menu->getItem($query['Itemid']);
// Build the component route
$component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']);
$crouter = $this->getComponentRouter($component);
$parts = $crouter->build($query);
$tmp = trim(implode('/', $parts));
// Build the application route
if ($item !== null && $query['option'] === $item->component)
{
if (!$item->home)
{
$tmp = $tmp ? $item->route . '/' . $tmp : $item->route;
}
unset($query['Itemid']);
}
else
{
$tmp = 'component/' . substr($query['option'], 4) . '/' . $tmp;
}
// Get the route
if ($tmp)
{
$uri->setPath($uri->getPath() . '/' . $tmp);
}
// Unset unneeded query information
unset($query['option']);
// Set query again in the URI
$uri->setQuery($query);
}
/**
* Convert limits for pagination
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildPaginationData(&$router, &$uri)
{
$limitstart = $uri->getVar('limitstart');
if ($limitstart !== null)
{
$uri->setVar('start', (int) $uri->getVar('limitstart'));
$uri->delVar('limitstart');
}
}
/**
* Build the format of the request
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildFormat(&$router, &$uri)
{
$route = $uri->getPath();
// Identify format
if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $format = $uri->getVar('format', 'html'))
{
$route .= '.' . $format;
$uri->setPath($route);
$uri->delVar('format');
}
}
/**
* Create a uri based on a full or partial URL string
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildRewrite(&$router, &$uri)
{
// Get the path data
$route = $uri->getPath();
// Transform the route
if ($route === 'index.php')
{
$route = '';
}
else
{
$route = str_replace('index.php/', '', $route);
}
$uri->setPath($route);
}
/**
* Add the basepath to the URI
*
* @param SiteRouter &$router Router object
* @param Uri &$uri URI object to process
*
* @return void
*
* @since 4.0.0
*/
public function buildBase(&$router, &$uri)
{
// Add frontend basepath to the uri
$uri->setPath(Uri::root(true) . '/' . $uri->getPath());
}
/**
* Get component router
*
* @param string $component Name of the component including com_ prefix
*
* @return RouterInterface Component router
*
* @since 3.3
*/
public function getComponentRouter($component)
{
if (!isset($this->componentRouters[$component]))
{
$componentInstance = $this->app->bootComponent($component);
if ($componentInstance instanceof RouterServiceInterface)
{
$this->componentRouters[$component] = $componentInstance->createRouter($this->app, $this->menu);
}
if (!isset($this->componentRouters[$component]))
{
$this->componentRouters[$component] = new RouterLegacy(ucfirst(substr($component, 4)));
}
}
return $this->componentRouters[$component];
}
/**
* Set a router for a component
*
* @param string $component Component name with com_ prefix
* @param object $router Component router
*
* @return boolean True if the router was accepted, false if not
*
* @since 3.3
*/
public function setComponentRouter($component, $router)
{
$reflection = new \ReflectionClass($router);
if (\in_array('Joomla\\CMS\\Component\\Router\\RouterInterface', $reflection->getInterfaceNames()))
{
$this->componentRouters[$component] = $router;
return true;
}
else
{
return false;
}
}
}