rootPath **/ private $rootPath; /** * @var string $rootPath * Contains the application's root path (ex: /srv/http/myshop/) with trailing slash * Can be accessed read-only via $instance->rootUrl **/ private $rootUrl; /** * @var string $requestUrl * Contains request **/ private $requestUrl; /** * @var array ( array ( uri => controller ) ) $routes **/ private $routes; /** * @var \Tools\Context $context * /core/tools/Context.php * Contains website's informations **/ private $context; /** * @var array RouteParams * Contains route parameter * ex: /product/:id will contains { * 0 => product * 1 => [ID] * ':id' => [ID] **/ private $routeParams; /** * @var array $overridden * contains url and paths to rewrite **/ private $overridden; /** * @var string $modulePath * Contains the module directory **/ /** * @var string $moduleUrl * Contains the module Uri **/ /** * @var string $themePath * Contains the theme directory **/ /** * @var string $themeUrl * Contains the theme Uri **/ /** * Create the router, initialize url and path **/ public function __construct($server, $context) { $pos = strrpos($server["SCRIPT_NAME"], "/"); $relativePath = (($pos === FALSE) ? "" : substr($server["SCRIPT_NAME"], 0, $pos)); $this->rootPath = $server["DOCUMENT_ROOT"] . $relativePath . "/"; $this->rootUrl = $server["REQUEST_SCHEME"] . "://" . $server["HTTP_HOST"] . $relativePath ."/"; $this->requestUrl = substr($server["REQUEST_URI"], count($this->rootUrl) -1); $this->context = $context; $this->routes = array(); $this->overridden = null; } /** * Called after database initialization * Check the site url and redirect user if the HOST does not match * If the site url is not defined in database, do not redirect **/ public function init($server) { $siteUrl = \Entity\Config::getConfig(null, "siteUrl"); // @codeCoverageIgnoreStart // This code is tested under another process if ($siteUrl != $server["HTTP_HOST"] && $siteUrl !== null) { header("location: http://{$siteUrl}{$server['REQUEST_URI']}"); die; } // @codeCoverageIgnoreEnd } /** * @return \Controller\AController controller * /core/controller/AController.php * Match request to a controller * return FALSE on failure (eg. 404) **/ public function serveUrl() { $this->prepareUrl(); $requestParams = explode("/", $this->requestUrl); foreach ($this->routes as $i) { $routeParams = explode("/", $i[0]); $p = $this->routeMatch($requestParams, $routeParams); if ($p === false) continue; $controller = $this->createController($i[1], $p); if ($controller) return $controller; } return false; } /** * Create and return Controller for the given route * @param $className * @param array $routeParameters * @return \Tools\AController on succes, false otherwise **/ private function createController($className, $params) { if (!class_exists($className)) return false; $this->routeParams = $params; $result = null; try { $result = new $className($this->context, $params); if (!($result instanceof \Tools\AController)) return false; } catch (\Exception\Error404 $e) { return false; } return $result; } /** * Check if the request match route * @param array $request User request * @param array $route Route to check * @return array on success, false on failure **/ private function routeMatch($request, $route) { $i = count($request); $params = array(); if ($i != count($route)) return false; while ($i) { $i--; if ($route[$i] == '' && $request[$i] == '') continue; if ($route[$i] == '' || $request[$i] == '') return false; if ($route[$i][0] != ':' && ($route[$i] != $request[$i])) return false; if ($route[$i][0] == ':') $params[$route[$i]] = $request[$i]; $params[$i -1] = $request[$i]; } return array_reverse($params); } /** * Append local routes to router * Will load CMS pages, categories page, products page, cart pages, etc. **/ private function prepareUrl() { $fetcher = new \Entity\Cms(); $pages = $fetcher->selects(null, array("order")); foreach ($pages as $i) $this->doRouteAdd($i->shurl, $i->controller); } /** * Add a route to the internal route list * Internal procedure **/ private function doRouteAdd($route, $controller) { $this->routes[] = array($route, $controller); } /** * @param string $route Uri to match the controller * Uri can be formatted as '/:param/static'. * expl. '/product/:id/' * @param string $controller Controller class name. * new $controller() MUST return a \Tool\AController instance * * Add a route and a Controller to the list * Can only be called from `routerSetup' hook **/ public function routeAdd($route, $controller) { if (!$this->context->hookManager->isInHook("routerSetup")) throw new \Exception("You can only add routes from `routerSetup' hook"); $this->doRouteAdd($route, $controller); } /** * Override url * This SHOULD not be called for security purpose * @param string $type the url type to override * @param string $value the new value * Will fail if called from a controller * @return boolean false on failure **/ public function overrideUrl($type, $value) { if (!$this->context->isTestingEnvironment()) return false; $this->overridden[$type] = $value; return true; } /** * Getter **/ public function __get($key) { if (isset($this->overridden) && in_array($key, array("modulesPath"))) return $this->overridden[$key]; switch ($key) { case "rootPath": return $this->rootPath; break; case "rootUrl": return $this->rootUrl; break; case "modulesPath": return $this->rootPath."content/modules/"; break; case "modulesUrl": return $this->rootUrl."content/modules/"; break; case "themesPath": return $this->rootPath."content/theme/"; break; case "themesUrl": return $this->rootUrl."content/theme/"; break; } throw new \Exception("Cannot access attribute {$key}"); } }