lbry.com/lib/routing/Dispatcher.class.php
2019-03-21 14:29:40 -04:00

235 lines
6.2 KiB
PHP

<?php
namespace Routing;
class Dispatcher
{
private $staticRouteMap;
private $variableRouteData;
private $filters;
private $handlerResolver;
public $matchedRoute;
/**
* Create a new route dispatcher.
*
* @param RouteDataInterface $data
* @param HandlerResolverInterface $resolver
*/
public function __construct(RouteData $data, HandlerResolverInterface $resolver = null)
{
$this->staticRouteMap = $data->getStaticRoutes();
$this->variableRouteData = $data->getVariableRoutes();
$this->filters = $data->getFilters();
if ($resolver === null) {
$this->handlerResolver = new HandlerResolver();
} else {
$this->handlerResolver = $resolver;
}
}
/**
* Dispatch a route for the given HTTP Method / URI.
*
* @param $httpMethod
* @param $uri
*
* @return mixed|null
*/
public function dispatch($httpMethod, $uri)
{
list($handler, $filters, $vars) = $this->dispatchRoute($httpMethod, trim($uri, '/'));
list($beforeFilter, $afterFilter) = $this->parseFilters($filters);
if (($response = $this->dispatchFilters($beforeFilter)) !== null) {
return $response;
}
$resolvedHandler = $this->handlerResolver->resolve($handler);
$response = call_user_func_array($resolvedHandler, $vars);
return $this->dispatchFilters($afterFilter, $response);
}
public function hasMatchingRouteForUri($httpMethod, $uri)
{
if (isset($this->staticRouteMap[$uri])) {
$routes = $this->staticRouteMap[$uri];
try {
if (!isset($routes[$httpMethod])) {
$httpMethod = $this->checkFallbacks($routes, $httpMethod);
}
} catch (HttpMethodNotAllowedException $e) {
return false;
}
return (boolean)$routes[$httpMethod];
}
try {
$handler = $this->dispatchVariableRoute($httpMethod, $uri);
return (boolean)$handler;
} catch (HttpRouteNotFoundException | HttpMethodNotAllowedException $e) {
return false;
}
return false;
}
/**
* Dispatch a route filter.
*
* @param $filters
* @param null $response
*
* @return mixed|null
*/
private function dispatchFilters($filters, $response = null)
{
while ($filter = array_shift($filters)) {
$handler = $this->handlerResolver->resolve($filter);
if (($filteredResponse = call_user_func($handler, $response)) !== null) {
return $filteredResponse;
}
}
return $response;
}
/**
* Normalise the array filters attached to the route and merge with any global filters.
*
* @param $filters
*
* @return array
*/
private function parseFilters($filters)
{
$beforeFilter = [];
$afterFilter = [];
if (isset($filters[Route::BEFORE])) {
$beforeFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::BEFORE]));
}
if (isset($filters[Route::AFTER])) {
$afterFilter = array_intersect_key($this->filters, array_flip((array)$filters[Route::AFTER]));
}
return [$beforeFilter, $afterFilter];
}
/**
* Perform the route dispatching. Check static routes first followed by variable routes.
*
* @param $httpMethod
* @param $uri
*
* @throws Exception\HttpRouteNotFoundException
*/
private function dispatchRoute($httpMethod, $uri)
{
if (isset($this->staticRouteMap[$uri])) {
return $this->dispatchStaticRoute($httpMethod, $uri);
}
return $this->dispatchVariableRoute($httpMethod, $uri);
}
/**
* Handle the dispatching of static routes.
*
* @param $httpMethod
* @param $uri
*
* @return mixed
* @throws Exception\HttpMethodNotAllowedException
*/
private function dispatchStaticRoute($httpMethod, $uri)
{
$routes = $this->staticRouteMap[$uri];
if (!isset($routes[$httpMethod])) {
$httpMethod = $this->checkFallbacks($routes, $httpMethod);
}
return $routes[$httpMethod];
}
/**
* Check fallback routes: HEAD for GET requests followed by the ANY attachment.
*
* @param $routes
* @param $httpMethod
*
* @throws Exception\HttpMethodNotAllowedException
*/
private function checkFallbacks($routes, $httpMethod)
{
$additional = [Route::ANY];
if ($httpMethod === Route::HEAD) {
$additional[] = Route::GET;
}
foreach ($additional as $method) {
if (isset($routes[$method])) {
return $method;
}
}
$this->matchedRoute = $routes;
throw new HttpMethodNotAllowedException(array_keys($routes), 'Method not allowed');
}
/**
* Handle the dispatching of variable routes.
*
* @param $httpMethod
* @param $uri
*
* @throws Exception\HttpMethodNotAllowedException
* @throws Exception\HttpRouteNotFoundException
*/
private function dispatchVariableRoute($httpMethod, $uri)
{
foreach ($this->variableRouteData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
$count = count($matches);
while (!isset($data['routeMap'][$count++])) {
;
}
$routes = $data['routeMap'][$count - 1];
if (!isset($routes[$httpMethod])) {
$httpMethod = $this->checkFallbacks($routes, $httpMethod);
}
foreach (array_values($routes[$httpMethod][2]) as $i => $varName) {
if (!isset($matches[$i + 1]) || $matches[$i + 1] === '') {
unset($routes[$httpMethod][2][$varName]);
} else {
$routes[$httpMethod][2][$varName] = $matches[$i + 1];
}
}
return $routes[$httpMethod];
}
throw new HttpRouteNotFoundException('Route ' . $uri . ' does not exist');
}
}