mirror of https://github.com/joomla/joomla-cms.git
183 lines
4.8 KiB
PHP
183 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* Joomla! Content Management System
|
|
*
|
|
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
|
|
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
|
*/
|
|
|
|
namespace Joomla\CMS\Router;
|
|
|
|
\defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Application\CMSApplicationInterface;
|
|
use Joomla\CMS\Router\Exception\RouteNotFoundException;
|
|
use Joomla\CMS\Uri\Uri;
|
|
use Joomla\Router\Route;
|
|
use Joomla\Router\Router;
|
|
|
|
/**
|
|
* Joomla! API Router class
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
class ApiRouter extends Router
|
|
{
|
|
/**
|
|
* The application object
|
|
*
|
|
* @var CMSApplicationInterface
|
|
* @since 4.0.0
|
|
*/
|
|
protected $app;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param CMSApplicationInterface $app The application object
|
|
* @param array $maps An optional array of route maps
|
|
*
|
|
* @since 1.0
|
|
*/
|
|
public function __construct(CMSApplicationInterface $app, array $maps = [])
|
|
{
|
|
$this->app = $app;
|
|
|
|
parent::__construct($maps);
|
|
}
|
|
|
|
/**
|
|
* Creates routes map for CRUD
|
|
*
|
|
* @param string $baseName The base name of the component.
|
|
* @param string $controller The name of the controller that contains CRUD functions.
|
|
* @param array $defaults An array of default values that are used when the URL is matched.
|
|
* @param bool $publicGets Allow the public to make GET requests.
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
public function createCRUDRoutes($baseName, $controller, $defaults = [], $publicGets = false)
|
|
{
|
|
$getDefaults = array_merge(['public' => $publicGets], $defaults);
|
|
|
|
$routes = [
|
|
new Route(['GET'], $baseName, $controller . '.displayList', [], $getDefaults),
|
|
new Route(['GET'], $baseName . '/:id', $controller . '.displayItem', ['id' => '(\d+)'], $getDefaults),
|
|
new Route(['POST'], $baseName, $controller . '.add', [], $defaults),
|
|
new Route(['PATCH'], $baseName . '/:id', $controller . '.edit', ['id' => '(\d+)'], $defaults),
|
|
new Route(['DELETE'], $baseName . '/:id', $controller . '.delete', ['id' => '(\d+)'], $defaults),
|
|
];
|
|
|
|
$this->addRoutes($routes);
|
|
}
|
|
|
|
/**
|
|
* Parse the given route and return the name of a controller mapped to the given route.
|
|
*
|
|
* @param string $method Request method to match. One of GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE or PATCH
|
|
*
|
|
* @return array An array containing the controller and the matched variables.
|
|
*
|
|
* @since 4.0.0
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public function parseApiRoute($method = 'GET')
|
|
{
|
|
$method = strtoupper($method);
|
|
|
|
$validMethods = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "PATCH"];
|
|
|
|
if (!\in_array($method, $validMethods))
|
|
{
|
|
throw new \InvalidArgumentException(sprintf('%s is not a valid HTTP method.', $method));
|
|
}
|
|
|
|
// Get the path from the route and remove and leading or trailing slash.
|
|
$uri = Uri::getInstance();
|
|
$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));
|
|
|
|
// Transform the route
|
|
$path = $this->removeIndexPhpFromPath($path);
|
|
|
|
$query = Uri::getInstance()->getQuery(true);
|
|
|
|
// Iterate through all of the known routes looking for a match.
|
|
foreach ($this->routes as $route)
|
|
{
|
|
if (\in_array($method, $route->getMethods()))
|
|
{
|
|
if (preg_match($route->getRegex(), ltrim($path, '/'), $matches))
|
|
{
|
|
// If we have gotten this far then we have a positive match.
|
|
$vars = $route->getDefaults();
|
|
|
|
foreach ($route->getRouteVariables() as $i => $var)
|
|
{
|
|
$vars[$var] = $matches[$i + 1];
|
|
}
|
|
|
|
$controller = preg_split("/[.]+/", $route->getController());
|
|
$vars = array_merge($vars, $query);
|
|
|
|
return [
|
|
'controller' => $controller[0],
|
|
'task' => $controller[1],
|
|
'vars' => $vars
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new RouteNotFoundException(sprintf('Unable to handle request for route `%s`.', $path));
|
|
}
|
|
|
|
/**
|
|
* Removes the index.php from the route's path.
|
|
*
|
|
* @param string $path
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
private function removeIndexPhpFromPath(string $path): string
|
|
{
|
|
// Normalize the path
|
|
$path = ltrim($path, '/');
|
|
|
|
// We can only remove index.php if it's present in the beginning of the route
|
|
if (strpos($path, 'index.php') !== 0)
|
|
{
|
|
return $path;
|
|
}
|
|
|
|
// Edge case: the route is index.php without a trailing slash. Bad idea but we can still map it to a null route.
|
|
if ($path === 'index.php')
|
|
{
|
|
return '';
|
|
}
|
|
|
|
// Remove the "index.php/" part of the route and return the result.
|
|
return substr($path, 10);
|
|
}
|
|
}
|