# Code Coverage for /win/work/Project/ecom/core/tools/Router.php

| Code Coverage
| Classes and Traits | Functions and Methods | Lines
Total | 0.00% covered (danger) | 0.00% | 0 / 1 | 88.89% covered (warning) | 88.89% | 8 / 9 | CRAP | 93.94% covered (success) | 93.94% | 62 / 66
Router | 0.00% covered (danger) | 0.00% | 0 / 1 | 88.89% covered (warning) | 88.89% | 8 / 9 | 35.27 | 93.94% covered (success) | 93.94% | 62 / 66
__construct | 100.00% covered (success) | 100.00% | 1 / 1 | 2 | 100.00% covered (success) | 100.00% | 8 / 8
init | 100.00% covered (success) | 100.00% | 1 / 1 | 3 | 100.00% covered (success) | 100.00% | 2 / 2
serveUrl | 100.00% covered (success) | 100.00% | 1 / 1 | 4 | 100.00% covered (success) | 100.00% | 11 / 11
createController | 100.00% covered (success) | 100.00% | 1 / 1 | 4 | 100.00% covered (success) | 100.00% | 10 / 10
routeMatch | 100.00% covered (success) | 100.00% | 1 / 1 | 10 | 100.00% covered (success) | 100.00% | 17 / 17
prepareUrl | 100.00% covered (success) | 100.00% | 1 / 1 | 2 | 100.00% covered (success) | 100.00% | 5 / 5
doRouteAdd | 100.00% covered (success) | 100.00% | 1 / 1 | 1 | 100.00% covered (success) | 100.00% | 2 / 2
routeAdd | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 | 0.00% covered (danger) | 0.00% | 0 / 4
__get | 100.00% covered (success) | 100.00% | 1 / 1 | 7 | 100.00% covered (success) | 100.00% | 7 / 7

1 | <?php
2 |
3 | namespace Tools;
4 |
5 | /**
6 | * Will manager new connections to the server
7 | * and try to match the requests and the controllers
8 | **/
9 | class Router
10 | {
11 | /**
12 | * @var string $rootPath
13 | * Contains the application's root path (ex: http://myshop.com/) with trailing slash
14 | * Can be accessed read-only via $instance->rootPath
15 | **/
16 | private $rootPath;
17 |
18 | /**
19 | * @var string $rootPath
20 | * Contains the application's root path (ex: /srv/http/myshop/) with trailing slash
21 | * Can be accessed read-only via $instance->rootUrl
22 | **/
23 | private $rootUrl;
24 |
25 | /**
26 | * @var string $requestUrl
27 | * Contains request
28 | **/
29 | private $requestUrl;
30 |
31 | /**
32 | * @var array ( array ( uri => controller ) ) $routes
33 | **/
34 | private $routes;
35 |
36 | /**
37 | * @var \Tools\Context $context
38 | * /core/tools/Context.php
39 | * Contains website's informations
40 | **/
41 | private $context;
42 |
43 | /**
44 | * @var array RouteParams
45 | * Contains route parameter
46 | * ex: /product/:id will contains {
47 | * 0 => product
48 | * 1 => [ID]
49 | * ':id' => [ID]
50 | **/
51 | private $routeParams;
52 |
53 | /**
54 | * @var string $modulePath
55 | * Contains the module directory
56 | **/
57 |
58 | /**
59 | * @var string $moduleUrl
60 | * Contains the module Uri
61 | **/
62 |
63 | /**
64 | * @var string $themePath
65 | * Contains the theme directory
66 | **/
67 |
68 | /**
69 | * @var string $themeUrl
70 | * Contains the theme Uri
71 | **/
72 |
73 | /**
74 | * Create the router, initialize url and path
75 | **/
76 | public function __construct($server, $context)
77 | {
78 | $pos = strrpos($server["SCRIPT_NAME"], "/");
79 | $relativePath = (($pos === FALSE) ? "" : substr($server["SCRIPT_NAME"], 0, $pos));
80 | $this->rootPath = $server["DOCUMENT_ROOT"] . $relativePath . "/";
81 | $this->rootUrl = $server["REQUEST_SCHEME"] . "://" . $server["HTTP_HOST"] . $relativePath ."/";
82 | $this->requestUrl = substr($server["REQUEST_URI"], count($this->rootUrl) -1);
83 | $this->context = $context;
84 | $this->routes = array();
85 | }
86 |
87 | /**
88 | * Called after database initialization
89 | * Check the site url and redirect user if the HOST does not match
90 | * If the site url is not defined in database, do not redirect
91 | **/
92 | public function init($server)
93 | {
94 | $siteUrl = \Entity\Config::getConfig(null, "siteUrl");
95 | // @codeCoverageIgnoreStart
96 | // This code is tested under another process
97 | if ($siteUrl != $server["HTTP_HOST"] && $siteUrl !== null)
98 | {
99 | header("location: http://{$siteUrl}{$server['REQUEST_URI']}");
100 | die;
101 | }
102 | // @codeCoverageIgnoreEnd
103 | }
104 |
105 | /**
106 | * @return \Controller\AController controller
107 | * /core/controller/AController.php
108 | * Match request to a controller
109 | * return FALSE on failure (eg. 404)
110 | **/
111 | public function serveUrl()
112 | {
113 | //TODO trigger hook GET, POST
114 | $this->prepareUrl();
115 | $requestParams = explode("/", $this->requestUrl);
116 | foreach ($this->routes as $i)
117 | {
118 | $routeParams = explode("/", $i[0]);
119 | $p = $this->routeMatch($requestParams, $routeParams);
120 | if ($p === false)
121 | continue;
122 | $controller = $this->createController($i[1], $p);
123 | if ($controller)
124 | return $controller;
125 | }
126 | return false;
127 | }
128 |
129 | /**
130 | * Create and return Controller for the given route
131 | * @param $className
132 | * @param array $routeParameters
133 | * @return \Tools\AController on succes, false otherwise
134 | **/
135 | private function createController($className, $params)
136 | {
137 | if (!class_exists($className))
138 | return false;
139 | $this->routeParams = $params;
140 | $result = null;
141 | try
142 | {
143 | $result = new $className($this->context, $params);
144 | if (!($result instanceof \Tools\AController))
145 | return false;
146 | }
147 | catch (\Exception\Error404 $e)
148 | {
149 | return false;
150 | }
151 | return $result;
152 | }
153 |
154 | /**
155 | * Check if the request match route
156 | * @param array $request User request
157 | * @param array $route Route to check
158 | * @return array on success, false on failure
159 | **/
160 | private function routeMatch($request, $route)
161 | {
162 | $i = count($request);
163 | $params = array();
164 | if ($i != count($route))
165 | return false;
166 | while ($i)
167 | {
168 | $i--;
169 | if ($route[$i] == '' && $request[$i] == '')
170 | continue;
171 | if ($route[$i] == '' || $request[$i] == '')
172 | return false;
173 | if ($route[$i][0] != ':' && ($route[$i] != $request[$i]))
174 | return false;
175 | if ($route[$i][0] == ':')
176 | $params[$route[$i]] = $request[$i];
177 | $params[$i -1] = $request[$i];
178 | }
179 | return array_reverse($params);
180 | }
181 |
182 | /**
183 | * Append local routes to router
184 | * Will load CMS pages, categories page, products page, cart pages, etc.
185 | **/
186 | private function prepareUrl()
187 | {
188 | $fetcher = new \Entity\Cms();
189 | $pages = $fetcher->selects(null, array("order"));
190 | foreach ($pages as $i)
191 | $this->doRouteAdd($i->shurl, $i->controller);
192 | }
193 |
194 | /**
195 | * Add a route to the internal route list
196 | * Internal procedure
197 | **/
198 | private function doRouteAdd($route, $controller)
199 | {
200 | $this->routes[] = array($route, $controller);
201 | }
202 |
203 | /**
204 | * @param string $route Uri to match the controller
205 | * Uri can be formatted as '/:param/static'.
206 | * expl. '/product/:id/'
207 | * @param string $controller Controller class name.
208 | * new $controller() MUST return a \Tool\AController instance
209 | *
210 | * Add a route and a Controller to the list
211 | * Can only be called from `routerSetup' hook
212 | **/
213 | public function routeAdd($route, $controller)
214 | {
215 | if (!$this->context->hookManager->isInHook("routerSetup"))
216 | throw new \Exception("You can only add routes from `routerSetup' hook");
217 | $this->doRouteAdd($route, $controller);
218 | }
219 |
220 | /**
221 | * Getter
222 | **/
223 | public function __get($key)
224 | {
225 | switch ($key)
226 | {
227 | case "rootPath": return $this->rootPath; break;
228 | case "rootUrl": return $this->rootUrl; break;
229 | case "modulesPath": return $this->rootPath."content/modules/"; break;
230 | case "modulesUrl": return $this->rootUrl."content/modules/"; break;
231 | case "themesPath": return $this->rootPath."content/theme/"; break;
232 | case "themesUrl": return $this->rootUrl."content/theme/"; break;
233 | }
234 | throw new \Exception("Cannot access attribute {$key}");
235 | }
236 | }
237 |