| Code Coverage
| Classes and Traits | Functions and Methods | Lines
Total | 0.00% covered (danger) | 0.00% | 0 / 1 | 90.00% covered (success) | 90.00% | 9 / 10 | CRAP | 94.52% covered (success) | 94.52% | 69 / 73
Router | 0.00% covered (danger) | 0.00% | 0 / 1 | 90.00% covered (success) | 90.00% | 9 / 10 | 39.25 | 94.52% covered (success) | 94.52% | 69 / 73
__construct | 100.00% covered (success) | 100.00% | 1 / 1 | 2 | 100.00% covered (success) | 100.00% | 9 / 9
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
overrideUrl | 100.00% covered (success) | 100.00% | 1 / 1 | 2 | 100.00% covered (success) | 100.00% | 4 / 4
__get | 100.00% covered (success) | 100.00% | 1 / 1 | 9 | 100.00% covered (success) | 100.00% | 9 / 9

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 array $overridden
55 | * contains url and paths to rewrite
56 | **/
57 | private $overridden;
58 |
59 | /**
60 | * @var string $modulePath
61 | * Contains the module directory
62 | **/
63 |
64 | /**
65 | * @var string $moduleUrl
66 | * Contains the module Uri
67 | **/
68 |
69 | /**
70 | * @var string $themePath
71 | * Contains the theme directory
72 | **/
73 |
74 | /**
75 | * @var string $themeUrl
76 | * Contains the theme Uri
77 | **/
78 |
79 | /**
80 | * Create the router, initialize url and path
81 | **/
82 | public function __construct($server, $context)
83 | {
84 | $pos = strrpos($server["SCRIPT_NAME"], "/");
85 | $relativePath = (($pos === FALSE) ? "" : substr($server["SCRIPT_NAME"], 0, $pos));
86 | $this->rootPath = $server["DOCUMENT_ROOT"] . $relativePath . "/";
87 | $this->rootUrl = $server["REQUEST_SCHEME"] . "://" . $server["HTTP_HOST"] . $relativePath ."/";
88 | $this->requestUrl = substr($server["REQUEST_URI"], count($this->rootUrl) -1);
89 | $this->context = $context;
90 | $this->routes = array();
91 | $this->overridden = null;
92 | }
93 |
94 | /**
95 | * Called after database initialization
96 | * Check the site url and redirect user if the HOST does not match
97 | * If the site url is not defined in database, do not redirect
98 | **/
99 | public function init($server)
100 | {
101 | $siteUrl = \Entity\Config::getConfig(null, "siteUrl");
102 | // @codeCoverageIgnoreStart
103 | // This code is tested under another process
104 | if ($siteUrl != $server["HTTP_HOST"] && $siteUrl !== null)
105 | {
106 | header("location: http://{$siteUrl}{$server['REQUEST_URI']}");
107 | die;
108 | }
109 | // @codeCoverageIgnoreEnd
110 | }
111 |
112 | /**
113 | * @return \Controller\AController controller
114 | * /core/controller/AController.php
115 | * Match request to a controller
116 | * return FALSE on failure (eg. 404)
117 | **/
118 | public function serveUrl()
119 | {
120 | //TODO trigger hook GET, POST
121 | $this->prepareUrl();
122 | $requestParams = explode("/", $this->requestUrl);
123 | foreach ($this->routes as $i)
124 | {
125 | $routeParams = explode("/", $i[0]);
126 | $p = $this->routeMatch($requestParams, $routeParams);
127 | if ($p === false)
128 | continue;
129 | $controller = $this->createController($i[1], $p);
130 | if ($controller)
131 | return $controller;
132 | }
133 | return false;
134 | }
135 |
136 | /**
137 | * Create and return Controller for the given route
138 | * @param $className
139 | * @param array $routeParameters
140 | * @return \Tools\AController on succes, false otherwise
141 | **/
142 | private function createController($className, $params)
143 | {
144 | if (!class_exists($className))
145 | return false;
146 | $this->routeParams = $params;
147 | $result = null;
148 | try
149 | {
150 | $result = new $className($this->context, $params);
151 | if (!($result instanceof \Tools\AController))
152 | return false;
153 | }
154 | catch (\Exception\Error404 $e)
155 | {
156 | return false;
157 | }
158 | return $result;
159 | }
160 |
161 | /**
162 | * Check if the request match route
163 | * @param array $request User request
164 | * @param array $route Route to check
165 | * @return array on success, false on failure
166 | **/
167 | private function routeMatch($request, $route)
168 | {
169 | $i = count($request);
170 | $params = array();
171 | if ($i != count($route))
172 | return false;
173 | while ($i)
174 | {
175 | $i--;
176 | if ($route[$i] == '' && $request[$i] == '')
177 | continue;
178 | if ($route[$i] == '' || $request[$i] == '')
179 | return false;
180 | if ($route[$i][0] != ':' && ($route[$i] != $request[$i]))
181 | return false;
182 | if ($route[$i][0] == ':')
183 | $params[$route[$i]] = $request[$i];
184 | $params[$i -1] = $request[$i];
185 | }
186 | return array_reverse($params);
187 | }
188 |
189 | /**
190 | * Append local routes to router
191 | * Will load CMS pages, categories page, products page, cart pages, etc.
192 | **/
193 | private function prepareUrl()
194 | {
195 | $fetcher = new \Entity\Cms();
196 | $pages = $fetcher->selects(null, array("order"));
197 | foreach ($pages as $i)
198 | $this->doRouteAdd($i->shurl, $i->controller);
199 | }
200 |
201 | /**
202 | * Add a route to the internal route list
203 | * Internal procedure
204 | **/
205 | private function doRouteAdd($route, $controller)
206 | {
207 | $this->routes[] = array($route, $controller);
208 | }
209 |
210 | /**
211 | * @param string $route Uri to match the controller
212 | * Uri can be formatted as '/:param/static'.
213 | * expl. '/product/:id/'
214 | * @param string $controller Controller class name.
215 | * new $controller() MUST return a \Tool\AController instance
216 | *
217 | * Add a route and a Controller to the list
218 | * Can only be called from `routerSetup' hook
219 | **/
220 | public function routeAdd($route, $controller)
221 | {
222 | if (!$this->context->hookManager->isInHook("routerSetup"))
223 | throw new \Exception("You can only add routes from `routerSetup' hook");
224 | $this->doRouteAdd($route, $controller);
225 | }
226 |
227 | /**
228 | * Override url
229 | * This SHOULD not be called for security purpose
230 | * @param string $type the url type to override
231 | * @param string $value the new value
232 | * Will fail if called from a controller
233 | * @return boolean false on failure
234 | **/
235 | public function overrideUrl($type, $value)
236 | {
237 | if (!$this->context->isTestingEnvironment())
238 | return false;
239 | $this->overridden[$type] = $value;
240 | return true;
241 | }
242 |
243 | /**
244 | * Getter
245 | **/
246 | public function __get($key)
247 | {
248 | if (isset($this->overridden) && in_array($key, array("modulesPath")))
249 | return $this->overridden[$key];
250 | switch ($key)
251 | {
252 | case "rootPath": return $this->rootPath; break;
253 | case "rootUrl": return $this->rootUrl; break;
254 | case "modulesPath": return $this->rootPath."content/modules/"; break;
255 | case "modulesUrl": return $this->rootUrl."content/modules/"; break;
256 | case "themesPath": return $this->rootPath."content/theme/"; break;
257 | case "themesUrl": return $this->rootUrl."content/theme/"; break;
258 | }
259 | throw new \Exception("Cannot access attribute {$key}");
260 | }
261 | }
262 |