namespace Ice\Mvc; use Ice\Di; use Ice\Exception; use Ice\Mvc\Route\Collector; use Ice\Mvc\Route\Dispatcher\DispatcherInterface; /** * Router is the standard framework router. Routing is the process of taking a URI endpoint and decomposing it into * parameters to determine which module, controller, and action of that controller should receive the request. * * @package Ice/Router * @category Component * @author Ice Team * @copyright (c) 2014-2023 Ice Team * @license http://iceframework.org/license * @uses FastRoute http:/github.com/nikic/FastRoute */ class FastRouter { protected defaultModule = "default" { get, set }; protected defaultHandler = "index" { get, set }; protected defaultAction = "index" { get, set }; protected method { get }; protected module{ get }; protected handler { get }; protected action { get }; protected params = [] { get }; protected ready = false; protected silent = false { set }; protected options = [] { get }; protected routes { get, set }; protected collector { get, set }; protected dispatcher { get, set }; const NOT_FOUND = 0; const FOUND = 1; const METHOD_NOT_ALLOWED = 2; /** * Set defaults values * * @param array defaults * @return object Router */ public function setDefaults(array! defaults) { var module, handler, action; if fetch module, defaults["module"] { let this->defaultModule = module; } if fetch handler, defaults["handler"] { let this->defaultHandler = handler; } if fetch action, defaults["action"] { let this->defaultAction = action; } return this; } /** * Set options. * * @param array options * @return object Router */ public function setOptions(array! options) { let this->options = options; return this; } /** * Prepare the FastRoute. * @return object Router */ public function fastRoute() { var options, data, route, handler; let options = array_merge([ "routeParser": "Ice\\Mvc\\Route\\Parser\\Std", "dataGenerator": "Ice\\Mvc\\Route\\DataGenerator\\GroupCount", "dispatcher": "Ice\\Mvc\\Route\\Dispatcher\\GroupCount", "cache": false ], this->options); let this->options = options; if typeof this->collector != "object" || typeof this->collector == "object" && !(this->collector instanceof Collector) { let this->collector = new Collector(create_instance(options["routeParser"]), create_instance(options["dataGenerator"])); } if !this->routes { // Set default routes let this->routes = [ ["*", "/{controller:[a-z]+}/{action:[a-z]+}[/{param}]"], ["*", "/{controller:[a-z]+}"], ["*", ""] ]; } for route in this->routes { fetch handler, route[2]; this->collector->addRoute(route[0], route[1], handler); } if typeof this->dispatcher != "object" || typeof this->dispatcher == "object" && !(this->dispatcher instanceof DispatcherInterface) { let this->dispatcher = create_instance(options["dispatcher"]); } if options["cache"] { if !isset options["cacheFile"] { throw new Exception("Must specify 'cacheFile' option"); } if file_exists(options["cacheFile"]) { let data = require options["cacheFile"]; } else { let data = this->collector->getData(); file_put_contents(options["cacheFile"], "collector->getData(); } this->dispatcher->setData(data); let this->ready = true; return this; } /** * Handles routing information received from the FastRoute engine. * * @param string uri * @return mixed */ public function handle(method = null, uri = null) { var module, handler, action, params, holders, data, route, response; let handler = this->defaultHandler, action = this->defaultAction, params = []; if !this->ready { this->fastRoute(); } let route = this->dispatcher->dispatch(method, uri); switch route[0] { case self::NOT_FOUND: if this->silent { // 404 Not Found let response = Di::$fetch()->get("response"); response->setStatus(404); response->setBody(response->getMessage(404)); return response; } throw new Exception("The requested route could not be found", self::NOT_FOUND); case self::METHOD_NOT_ALLOWED: if this->silent { // 405 Method Not Allowed let response = Di::$fetch()->get("response"); response->setStatus(405); response->setBody(response->getMessage(405)); return response; } throw new Exception("A request was made of a resource using a request method not supported by that resource", self::METHOD_NOT_ALLOWED); case self::FOUND: let holders = route[1], data = route[2]; if !fetch module, holders["module"] { if fetch module, data["module"] { let module = str_replace("/", "", module); unset data["module"]; } else { let module = this->defaultModule; } } if !fetch handler, holders["controller"] { if fetch handler, data["controller"] { let handler = str_replace("/", "", handler); unset data["controller"]; } else { let handler = this->defaultHandler; } } if !fetch action, holders["action"] { if fetch action, data["action"] { let action = str_replace("/", "", action); unset data["action"]; } else { let action = this->defaultAction; } } let params = data; break; } let this->method = method, this->module = module, this->handler = handler, this->action = action, this->params = params; return ["module": module, "handler": handler, "action": action, "params": params]; } }