<?php
namespace Lia\Addon;
/**
*
* Sets up routes & handles Response events, returning route lists, and printing responses
*
*/
class Router extends \Lia\Addon implements \Lia\Http\ResponseInterface {
/**
* Array of routers. Typically, one for each app
*/
protected array $routers = [];
/**
* The primary router, for manually adding routes.
*
* If you change this, you must also call `addRouter($new_main_router)`
*/
public \Lia\Http\Router $main_router;
/**
* The request pssed to onGetHttpRoutes().
*/
protected \Lia\Http\Request $request;
/**
* Add a route to the main router.
*
* @see Lia\Http\Router::addRoute()
* @see Lia\Addon\Router::$main_router
*
* @error if $main_router is not set. main_router is normally set during `onEnabledByApp`
*/
public function addRoute(string $pattern, mixed $target, array $methods=["GET"],array $args = []): void {
$this->main_router->addRoute($pattern,$target,$methods,$args);
}
/**
* Add an Http Router.
*
* @param $router \Lia\Http\Router an HTTP router.
*/
public function addRouter(\Lia\Http\Router $router){
$this->routers[] = $router;
}
/**
* Setup routes for an app.
*
* @param $app
* @param $public_dir string|bool `true` to use `public` dir. `false` to NOT enable. `string` to use a different app sub-directory.
*/
public function onEnabledByApp(\Lia\AppInterface $app, mixed $public_dir){
if ($public_dir===true)$public_dir= 'public';
else if ($public_dir===false)return;
else if (!is_string($public_dir)){
$ns = $app->getNamespace();
$class = get_class($app);
$type = gettype($public_dir);
throw new \Exception("App '$ns' (class '$class') enabled addon 'lia:router', but provided an invalid value. The value MUST be a boolean or string, but a '$type' was provided. The value was '$public_dir'");
}
// actually setup the routes
$router = new \Lia\Http\Router();
// @TODO configure params to pass to routes, on an app-by-app basis
// @TODO configure varDelim, on an app-by-app basis
$base_url = $app->hasConfig('router.base_url') ? $app->getConfig('router.base_url') : '/';
$router->allowExecutableFile = $app->hasConfig('router.allow_executable_file') ? $app->getConfig('router.allow_executable_file') : $router->allowExecutableFile;
$router->varDelim = $app->hasConfig('router.var_delimiter') ? $app->getConfig('router.var_delimiter') : $router->varDelim;
$router->theme_name = $app->hasConfig('router.default_theme') ? $app->getConfig('router.default_theme') : 'raw';
$router->route_args = [
'lia'=>$app->getLia(),
'app'=>$app,
'public_dir'=>$public_dir,
];
$router->addDirectoryRoutes($app->getPath($public_dir), $base_url);
if (!isset($this->main_router)){
$this->main_router = $router;
}
$this->routers[] = $router;
}
public function onGetHttpRoutes(\Lia\Http\Request $request, \Lia\Http\Response $response): array {
$this->request = $request;
$route_list = [];
/** @var $router \Lia\Http\Router */
foreach ($this->routers as $router){
foreach ($router->getRoutes($request->url,$request->method) as $route){
$route->router = $router;
$route_list[] = $route;
}
}
return $route_list;
}
public function onAddonsLoaded(\Lia\AppInterface $app, array $addons){
// @TODO maybe add a different callback for an addon to set itself up
$app->getLia()->hook(\Lia\Events::GetHttpRoutes->value, [$this, 'onGetHttpRoutes']);
$app->getLia()->hook(\Lia\Events::NoHttpRoutes->value, [$this, 'onNoHttpRoutes']);
$app->getLia()->hook(\Lia\Events::SingleHttpRoute->value, [$this, 'onSingleHttpRoute']);
$app->getLia()->hook(\Lia\Events::MultipleHttpRoutes->value, [$this, 'onMultipleHttpRoutes']);
$app->getLia()->hook(\Lia\Events::ApplyTheme->value, [$this, 'onApplyTheme']);
}
/** do nothing */
public function onStartRequest(\Lia\Http\Request $request, \Lia\Http\Response $response){}
/** Send a response */
public function onNoHttpRoutes(\Lia\Http\Request $request, \Lia\Http\Response $response){
$response->PageNotFound();
$response->body = "No page was found for your request to ".$request->url;
// How can no-route be over-ridden?
// Well, I can just have something else to subscribe to the event & modify the response.
// So lets try that.
}
/**
* Set a 500 internal server error header & response body indicating multiple routes were found & cannot be resolved.
*
* @param $routes array<int index, \Lia\Http\Route $route> an array of Routes
*/
public function onMultipleHttpRoutes(\Lia\Http\Request $request, \Lia\Http\Response $response, array $routes){
// TODO: Add some default or config-based handling of MultipleRoutes
$response->InternalServerError();
$safe_url = strip_tags($request->url);
$count = count($routes);
$response->body =
"<h1>Multiple Potential Responses</h1>"
."\n<p>This request has $count potential responses, and the server is not configured to select a response automatically.</p>"
."\n<p>The Website Administrator or Developer should consult the documentation and implement a fix, unless they like errors.</p>"
."\n<p>The requested url was '".$safe_url."'</p>";
;
}
/**
*
* @param $route \Lia\Http\Route
*/
public function onSingleHttpRoute(\Lia\Http\Request $request, \Lia\Http\Response $response, \Lia\Http\Route $route){
// @TODO: Figure out & document what params I'm passing to callable routes & file routes.
if (!isset($response->theme_name))$response->theme_name = $route->router->theme_name;
$responder = new \Lia\Http\Responder();
$responder->allowExecutableFile = $route->router->allowExecutableFile;
$responder->respond_to_route($request, $response, $route);
//var_dump($route);
//exit;
}
/**
* @param $routes array<int index, \Lia\Http\Route $route> an array of Routes
*/
public function onApplyTheme(\Lia\Http\Request $request, \Lia\Http\Response $response, array $routes){
// @TODO consider passing more params to theme views.
if (!isset($response->theme_name))$theme = 'raw';
else $theme = $response->theme_name;
// @TODO Create an enum for the built-in themes, moreso for documentation than internal use.
if ($theme=='raw')return;
if ($theme=='json')return;
if ($theme=='html_page'){
// apply built-in-theme
$response->body = $this->lia->view("lia:theme", ['content'=>$response->body]).'';
return;
}
$view = $this->lia->view($theme, ['content'=>$response->body]);
if (is_null($view)){
// throw??
// do nothing??
}
$response->body = "".$view;
}
/**
*
* @param $request The request that is finished
*/
public function onEndRequest(\Lia\Http\Request $request, \Lia\Http\Response $response, array $routes){
}
}