Hawk - PHP documentation
  • Namespace
  • Class
  • Tree

Namespaces

  • Hawk
    • View
      • Plugins

Classes

  • Hawk\App
  • Hawk\ButtonInput
  • Hawk\Cache
  • Hawk\CheckboxInput
  • Hawk\ColorInput
  • Hawk\Conf
  • Hawk\Controller
  • Hawk\Crypto
  • Hawk\DatabaseSessionHandler
  • Hawk\DatetimeInput
  • Hawk\DB
  • Hawk\DBExample
  • Hawk\DeleteInput
  • Hawk\Dialogbox
  • Hawk\EmailInput
  • Hawk\ErrorHandler
  • Hawk\Event
  • Hawk\FileInput
  • Hawk\FileSystem
  • Hawk\FloatInput
  • Hawk\Form
  • Hawk\FormFieldset
  • Hawk\FormInput
  • Hawk\GenericModel
  • Hawk\GifImage
  • Hawk\HawkApi
  • Hawk\HawkUpdater
  • Hawk\HiddenInput
  • Hawk\HtmlInput
  • Hawk\HTTPRequest
  • Hawk\Icon
  • Hawk\Image
  • Hawk\IntegerInput
  • Hawk\ItemList
  • Hawk\ItemListField
  • Hawk\JpegImage
  • Hawk\Lang
  • Hawk\Language
  • Hawk\LeftSidebarTab
  • Hawk\Less
  • Hawk\Logger
  • Hawk\Mail
  • Hawk\MenuItem
  • Hawk\Model
  • Hawk\NoSidebarTab
  • Hawk\NumberInput
  • Hawk\ObjectInput
  • Hawk\Option
  • Hawk\Panel
  • Hawk\PasswordInput
  • Hawk\Permission
  • Hawk\Plugin
  • Hawk\PluginInstaller
  • Hawk\PngImage
  • Hawk\ProfileQuestion
  • Hawk\ProfileQuestionValue
  • Hawk\RadioInput
  • Hawk\Request
  • Hawk\Response
  • Hawk\RightSidebarTab
  • Hawk\Role
  • Hawk\RolePermission
  • Hawk\Route
  • Hawk\Router
  • Hawk\SelectInput
  • Hawk\Session
  • Hawk\Singleton
  • Hawk\SubmitInput
  • Hawk\Tabs
  • Hawk\TextareaInput
  • Hawk\TextInput
  • Hawk\Theme
  • Hawk\TimeInput
  • Hawk\Upload
  • Hawk\User
  • Hawk\View
  • Hawk\View\Plugins\Accordion
  • Hawk\View\Plugins\Button
  • Hawk\View\Plugins\Form
  • Hawk\View\Plugins\Icon
  • Hawk\View\Plugins\Import
  • Hawk\View\Plugins\Panel
  • Hawk\View\Plugins\Tabs
  • Hawk\View\Plugins\Text
  • Hawk\View\Plugins\Uri
  • Hawk\View\Plugins\Widget
  • Hawk\ViewPlugin
  • Hawk\Widget
  • Hawk\WysiwygInput

Traits

  • Hawk\Utils

Exceptions

  • Hawk\AppStopException
  • Hawk\DBExampleException
  • Hawk\DBException
  • Hawk\FileSystemException
  • Hawk\HawkApiException
  • Hawk\ImageException
  • Hawk\MailException
  • Hawk\UploadException
  • Hawk\ViewException
  1 <?php
  2 /**
  3  * Router.php
  4  *
  5  * @author  Elvyrra SAS
  6  * @license http://rem.mit-license.org/ MIT
  7  */
  8 
  9 namespace Hawk;
 10 
 11 /**
 12  * This class describes the application router. It is used in any plugin to route URIs to controllers methods
 13  *
 14  * @package Core\Router
 15  */
 16 final class Router extends Singleton{
 17     /**
 18      * Invalid URL. This URI is displayed when no URI was found for a given route name
 19      */
 20     const INVALID_URL = '/INVALID_URL';
 21 
 22     /**
 23      * The defined routes
 24      *
 25      * @var array
 26      */
 27     private $routes = array(),
 28 
 29     /**
 30      * The routes accessible for the current request method
 31      */
 32     $activeRoutes = array(),
 33 
 34     /**
 35      * The current route, associated to the current URI
 36      */
 37     $currentRoute,
 38 
 39     /**
 40      * The current controller instance, associated to the current uri
 41      */
 42     $currentController,
 43 
 44     /**
 45      * The authentications required to match the URIs
 46      */
 47     $auth = array(),
 48 
 49 
 50     /**
 51      * The predefined data for the routes
 52      */
 53     $predefinedData = array();
 54 
 55 
 56     /**
 57      * The router instance
 58      */
 59     protected static $instance;
 60 
 61     /**
 62      * Add a new accessible route to the router
 63      *
 64      * @param string $method The HTTP method the route is accessible for
 65      * @param string $name   The route name. This name must be unique for each route
 66      * @param string $uri    The route URI, defined like : /path/{param1}/to/{param2}
 67      * @param array  $param  The route parameters. This array can have the following data :
 68      *                       <ul>
 69      *                           <li>'action' string (required) : The controller method to call when the route is matched,
 70      *                               formatted like this : 'ControllerClass.method'
 71      *                           </li>
 72      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
 73      *                               where keys are the names of the route parameters,
 74      *                               and values are the regular expression to match (without delimiters).
 75      *                           </li>
 76      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
 77      *                               This is useful to generate a URI from a route name (method getUri),
 78      *                               without giving all parameters values
 79      *                           </li>
 80      *                       </ul>
 81      */
 82     private function add($method, $name, $uri, $param){
 83         if(isset($param['auth'])) {
 84             $auth = $param['auth'];
 85             $param['auth'] = $this->auth;
 86             $param['auth'][] = $auth;
 87         }
 88         else{
 89             $param['auth'] = $this->auth;
 90         }
 91 
 92         foreach($this->predefinedData as $key => $value){
 93             $param[$key] = $value;
 94         }
 95 
 96         if(!isset($param['namespace'])) {
 97             $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 2);
 98             $param['namespace'] = Plugin::getFilePlugin($trace[1]['file'])->getNamespace();
 99         }
100 
101 
102         if(isset($this->routes[$name])) {
103             trigger_error("The route named '$name' already exists", E_USER_WARNING);
104         }
105         else{
106             $route = new Route($name, $uri, $param);
107 
108             $this->routes[$name] = &$route;
109 
110             if(App::request()->getMethod() == $method || $method == 'any') {
111                 $this->activeRoutes[$name] = &$route;
112             }
113         }
114     }
115 
116     /**
117      * Add an authentication condition to match the routes defined inside $action callback.
118      * For example, you can write something like :
119      *
120      * <code>
121      * App::router()->auth(App::session()->getUser()->isAllowed('admin.all'), function(){
122      *        App::router()->get('test-route', '/test', array('action' => 'TestController.testMethod'));
123      * });
124      * </code>
125      *
126      * If the user tries to access to /test without the necessary privileges,
127      * then a HTTP code 403 (Forbidden) will be returned
128      *
129      * @param boolean  $auth   The authentication. If true, then the routes inside are accessible, else they're not
130      * @param callable $action The function that defines the routes under this authentication
131      */
132     public function auth($auth, $action){
133         // Add the authentication for all following route
134         $this->auth[] = $auth;
135 
136         // Execute the action
137         $action();
138 
139         // Remove the authentication for the rest of the scripts
140         array_pop($this->auth);
141     }
142 
143 
144     /**
145      * Set properties for all the routes that are defined in the $action callback.
146      * It can be used to set a prefix to a set of routes, a namespace for all routes actions, ...
147      *
148      * @param array    $data   The properties to set
149      * @param callable $action The function that defines the routes with these properties
150      */
151     public function setProperties($data, $action){
152         $currentData = $this->predefinedData;
153         foreach($data as $key => $value){
154             $this->predefinedData[$key] = $value;
155         }
156 
157         $action();
158 
159         $this->predefinedData = $currentData;
160     }
161 
162 
163     /**
164      * Set a prefix to routes URIs that are defined in $action callback
165      *
166      * @param string   $prefix The prefix to set to the URIs
167      * @param callable $action The function that defined the routes with this prefix
168      */
169     public function prefix($prefix, $action){
170         $this->setProperties(array('prefix' => $prefix), $action);
171     }
172 
173 
174     /**
175      * Add a route acessible by GET HTTP requests
176      *
177      * @param string $name  The route name. This name must be unique for each route
178      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
179      * @param array  $param The route parameters. This array can have the following data :
180      *                       <ul>
181      *                           <li>'action' string (required) : The controller method to call when the route is matched,
182      *                               formatted like this : 'ControllerClass.method'
183      *                           </li>
184      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
185      *                               where keys are the names of the route parameters,
186      *                               and values are the regular expression to match (without delimiters).
187      *                           </li>
188      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
189      *                               This is useful to generate a URI from a route name (method getUri),
190      *                               without giving all parameters values
191      *                           </li>
192      *                       </ul>
193      */
194     public function get($name, $path, $param){
195         $this->add('get', $name, $path, $param);
196     }
197 
198 
199     /**
200      * Add a route acessible by POST HTTP requests
201      *
202      * @param string $name  The route name. This name must be unique for each route
203      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
204      * @param array  $param The route parameters. This array can have the following data :
205      *                       <ul>
206      *                           <li>'action' string (required) : The controller method to call when the route is matched,
207      *                               formatted like this : 'ControllerClass.method'
208      *                           </li>
209      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
210      *                               where keys are the names of the route parameters,
211      *                               and values are the regular expression to match (without delimiters).
212      *                           </li>
213      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
214      *                               This is useful to generate a URI from a route name (method getUri),
215      *                               without giving all parameters values
216      *                           </li>
217      *                       </ul>
218      */
219     public function post($name, $path, $param){
220         $this->add('post', $name, $path, $param);
221     }
222 
223 
224     /**
225      * Add a route acessible by PUT HTTP requests
226      *
227      * @param string $name  The route name. This name must be unique for each route
228      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
229      * @param array  $param The route parameters. This array can have the following data :
230      *                       <ul>
231      *                           <li>'action' string (required) : The controller method to call when the route is matched,
232      *                               formatted like this : 'ControllerClass.method'
233      *                           </li>
234      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
235      *                               where keys are the names of the route parameters,
236      *                               and values are the regular expression to match (without delimiters).
237      *                           </li>
238      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
239      *                               This is useful to generate a URI from a route name (method getUri),
240      *                               without giving all parameters values
241      *                           </li>
242      *                       </ul>
243      */
244     public function put($name, $path, $param){
245         $this->add('put', $name, $path, $param);
246     }
247 
248 
249     /**
250      * Add a route acessible by DELETE HTTP requests
251      *
252      * @param string $name  The route name. This name must be unique for each route
253      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
254      * @param array  $param The route parameters. This array can have the following data :
255      *                       <ul>
256      *                           <li>'action' string (required) : The controller method to call when the route is matched,
257      *                               formatted like this : 'ControllerClass.method'
258      *                           </li>
259      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
260      *                               where keys are the names of the route parameters,
261      *                               and values are the regular expression to match (without delimiters).
262      *                           </li>
263      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
264      *                               This is useful to generate a URI from a route name (method getUri),
265      *                               without giving all parameters values
266      *                           </li>
267      *                       </ul>
268      */
269     public function delete($name, $path, $param){
270         $this->add('delete', $name, $path, $param);
271     }
272 
273 
274     /**
275      * Add a route acessible by PATCH HTTP requests
276      *
277      * @param string $name  The route name. This name must be unique for each route
278      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
279      * @param array  $param The route parameters. This array can have the following data :
280      *                       <ul>
281      *                           <li>'action' string (required) : The controller method to call when the route is matched,
282      *                               formatted like this : 'ControllerClass.method'
283      *                           </li>
284      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
285      *                               where keys are the names of the route parameters,
286      *                               and values are the regular expression to match (without delimiters).
287      *                           </li>
288      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
289      *                               This is useful to generate a URI from a route name (method getUri),
290      *                               without giving all parameters values
291      *                           </li>
292      *                       </ul>
293      */
294     public function patch($name, $path, $param){
295         $this->add('patch', $name, $path, $param);
296     }
297 
298     /**
299      * Add a route acessible by GET, POST OR DELETE HTTP requests
300      *
301      * @param string $name  The route name. This name must be unique for each route
302      * @param string $path  The route path, defined like : /path/{param1}/to/{param2}
303      * @param array  $param The route parameters. This array can have the following data :
304      *                       <ul>
305      *                           <li>'action' string (required) : The controller method to call when the route is matched,
306      *                               formatted like this : 'ControllerClass.method'
307      *                           </li>
308      *                           <li>'where' array (optionnal) : An array defining each parameter pattern,
309      *                               where keys are the names of the route parameters,
310      *                               and values are the regular expression to match (without delimiters).
311      *                           </li>
312      *                           <li>'default' array (optionnal) : An array defining the default values of parameters.
313      *                               This is useful to generate a URI from a route name (method getUri),
314      *                               without giving all parameters values
315      *                           </li>
316      *                       </ul>
317      */
318     public function any($name, $path, $param){
319         $this->add('any', $name, $path, $param);
320     }
321 
322 
323     /**
324      * Compute the routing, and execute the controller method associated to the URI
325      */
326     public function route(){
327         $path = str_replace(BASE_PATH, '', parse_url(App::request()->getUri(), PHP_URL_PATH));
328 
329         // Scan each row
330         foreach($this->activeRoutes as $route){
331             if($route->match($path)) {
332                 // The URI matches with the route
333                 $this->currentRoute = &$route;
334 
335                 // Emit an event, saying the routing action is finished
336                 $event = new Event(
337                     'after-routing', array(
338                     'route' => $route,
339                     )
340                 );
341                 $event->trigger();
342 
343                 $route = $event->getData('route');
344 
345                 if($route->isAccessible()) {
346                     // The route authentications are validated
347 
348 
349                     list($classname, $method) = explode(".", $route->action);
350 
351                     // call a controller method
352                     $this->currentController = new $classname($route->getData());
353                     App::logger()->debug(sprintf(
354                         'URI %s has been routed => %s::%s',
355                         App::request()->getUri(),
356                         $classname,
357                         $method
358                     ));
359 
360                     // Set the controller result to the HTTP response
361                     App::response()->setBody($this->currentController->compute($method));
362                 }
363                 else{
364 
365                     // The route is not accessible
366                     App::logger()->warning(sprintf(
367                         'A user with the IP address %s tried to access %s without the necessary privileges',
368                         App::request()->clientIp(),
369                         App::request()->getUri()
370                     ));
371 
372                     App::response()->setStatus(403);
373                     $response = array(
374                         'message' => Lang::get('main.403-message'),
375                         'reason' => !App::session()->isLogged() ? 'login' : 'permission'
376                     );
377 
378                     if(App::request()->isAjax()) {
379                         App::response()->setContentType('json');
380                         App::response()->setBody($response);
381                     }
382                     else{
383                         App::response()->setBody($response['message']);
384                     }
385 
386                     throw new AppStopException();
387                 }
388                 return;
389             }
390         }
391 
392         // The route was not found
393         App::logger()->warning('The URI ' . App::request()->getUri() . ' has not been routed');
394         App::response()->setStatus(404);
395         App::response()->setBody(Lang::get('main.404-message', array('uri' => $path)));
396     }
397 
398 
399     /**
400      * Get all defined routes
401      *
402      * @return array The defined routes
403      */
404     public function getRoutes(){
405         return $this->routes;
406     }
407 
408     /**
409      * Get the routes accessible for the current HTTP request method
410      *
411      * @return array The list of the accessible routes
412      */
413     public function getActiveRoutes(){
414         return $this->activeRoutes;
415     }
416 
417 
418     /**
419      * Get the route corresponding to the current HTTP request
420      *
421      * @return Route The current route
422      */
423     public function getCurrentRoute(){
424         return isset($this->currentRoute) ? $this->currentRoute : null;
425     }
426 
427     /**
428      * Get the action parameter of the current route
429      *
430      * @return string The action of the current route
431      */
432     public function getCurrentAction(){
433         return isset($this->currentRoute) ? $this->currentRoute->action : '';
434     }
435 
436     /**
437      * Get the last instanciated controller
438      *
439      * @return Controller The last instanciated controller
440      */
441     public function getCurrentController(){
442         return $this->currentController;
443     }
444 
445     /**
446      * Generate an URI from a given controller method (or route name) and its arguments.
447      *
448      * @param string $name The route name of the controller method, formatted like this : 'ControllerClass.method'
449      * @param array  $args The route arguments, where keys define the parameters names and values, the values to affect.
450      *
451      * @return string The generated URI, or the current URI (if $method is not set)
452      */
453     public function getUri($name, $args= array()){
454 
455         $route = $this->getRouteByAction($name);
456 
457         if(empty($route)) {
458             return self::INVALID_URL;
459         }
460 
461         $url = $route->url;
462         foreach($route->args as $arg){
463             if(isset($args[$arg])) {
464                 $replace = $args[$arg];
465             }
466             elseif(isset($route->default[$arg])) {
467                 $replace = $route->default[$arg];
468             }
469             else{
470                 throw new \Exception("The URI built from '$method' needs the argument : $arg");
471             }
472             $url = str_replace("{{$arg}}", $replace, $url);
473         }
474 
475         return BASE_PATH . $url;
476     }
477 
478 
479     /**
480      * Generate a full URL from a given controller method (or route name) and its arguments.
481      *
482      * @param string $name The route name of the controller method, formatted like this : 'ControllerClass.method'
483      * @param array  $args The route arguments, where keys define the parameters names and values, the values to affect.
484      *
485      * @return string The generated URI, or the current URI (if $method is not set)
486      */
487     public function getUrl($name = '', $args = array()){
488         return ROOT_URL . $this->getUri($name, $args);
489     }
490 
491 
492     /**
493      * Get a route by action
494      *
495      * @param string $name The route name of the controller method, formatted like this : 'ControllerClass.method'
496      *
497      * @return Route The route corresponding to research
498      */
499     public function getRouteByAction($name){
500         $route = null;
501         if(isset($this->routes[$name])) {
502             return $this->routes[$name];
503         }
504 
505         return null;
506     }
507 
508 
509     /**
510      * Find a route from an URI
511      *
512      * @param string $path The path to search the associated route
513      *
514      * @return Route the found route
515      */
516     public function getRouteByUri($path){
517         foreach($this->routes as $route){
518             if($route->match($path)) {
519                 return $route;
520             }
521         }
522 
523         return null;
524     }
525 
526 
527     /**
528      * Get a route by it name
529      *
530      * @param string $name The route name
531      *
532      * @return Route The found route
533      */
534     public function getRouteByName($name){
535         return isset($this->routes[$name]) ? $this->routes[$name] : null;
536     }
537 
538 }
Hawk - PHP documentation API documentation generated by ApiGen