mirror of
https://github.com/LBRYFoundation/lbry.com.git
synced 2025-08-23 17:47:26 +00:00
235 lines
6.2 KiB
PHP
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 (bool)$routes[$httpMethod];
|
|
}
|
|
|
|
try {
|
|
$handler = $this->dispatchVariableRoute($httpMethod, $uri);
|
|
return (bool)$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');
|
|
}
|
|
}
|