ElkArte Community

Elk Development => Feature Discussion => Topic started by: Ant59 on August 05, 2015, 08:06:56 am

Title: Router and Dispatcher
Post by: Ant59 on August 05, 2015, 08:06:56 am
I've been working on tweaking the dispatcher to make use of the autoloader and namespaces to allow drop-n-play addons to hook actions from their isolated directories under addons/[1].

In doing some research on how PHP frameworks handle dispatching, I came across numerous examples of routers and dispatchers [2]. My knowledge of routers comes from the Golang scene, where I use routers as part of the Revel framework for my clients' websites [3]. It seems that many have created router and dispatcher classes for PHP, which I think would be suitable to use in ElkArte.

At the moment, we have a primitive dispatcher which calls a class and method (and includes the files before my changes), based on two GET parameters: action and subaction. However, I think it would be nicer to see a richer router which could pass parameters to action controllers from anywhere in the URL.

This is, of course, not something that should be considered until 2.0. I personally really like the way that Revel handles routing, and I would suggest following suit, but I'm wondering what others' opinions are on this?

Here's an example of what I mean:
Code: [Select]
$router = new Router();

$router->add('GET', '/', '\\ElkArte\\Controllers\\BoardIndex_Controller', 'action_boardindex');
$router->add('GET', '/activate', '\\ElkArte\\Controllers\\Register_Controller', 'action_activate');
$router->add('GET', '/movetopic/:id', '\\ElkArte\\Controllers\\MoveTopic_Controller', 'action_movetopic');
$router->add('POST', '/movetopic', '\\ElkArte\\Controllers\\MoveTopic_Controller', 'action_movetopic2');

// Fallback
$router->add('GET', '/:action', '\\ElkArte\\Controllers\\:action_Controller', 'action_index');
$router->add('GET', '/:action/:sa', '\\ElkArte\\Controllers\\:action_Controller', 'action_:sa');

This would get rid of all the fallback checks we have currently in the dispatcher and smarten everything up. Any path section leading with : is a variable which is passed to the controller, or even used in the discovery of the class and method to call. Then the dispatcher just checks if it's a 'callable' and if not, defaults to /, which in this case is the board index.

By using the correct HTTP methods to detect the type of action, we could do away with all these <action>2 url forms by looking for POST requests.

Addons could then just use the following to add routes with parameters:

Code: [Select]
$router->add('GET', '/page/:id', '\\ElkArte\\Addons\\Pages\\Pages_Controller', 'action_page');

With a router setup like this, we can implement a router method for reversal, to generate URLs from actions. This would be useful for templates/themes, where links are required, making the URL scheme more flexible, and more human-friendly if required [4].

My examples follow the Revel router style, but any format would do. The standardisation and flexibility of a router and dispatcher is the important part. In Elkarte, it might be best to use PHP's built-in regex format.
https://github.com/elkarte/Elkarte/pull/2169
such as https://github.com/chriso/klein.php
https://revel.github.io/manual/routing.html
https://github.com/elkarte/Elkarte/issues/2167
Title: Re: Router and Dispatcher
Post by: inter on August 06, 2015, 03:58:33 am
you have broken links.

Spoiler (click to show/hide)
Title: Re: Router and Dispatcher
Post by: Ant59 on August 06, 2015, 05:12:19 am

Oops, thanks! I've converted them to footnotes and reported the bug here: https://github.com/elkarte/Elkarte/issues/2171
Title: Re: Router and Dispatcher
Post by: Spuds on August 06, 2015, 06:32:55 am
/me likes this approach
Title: Re: Router and Dispatcher
Post by: Ant59 on September 09, 2015, 10:29:07 am
This is something I'm happy to go about attempting in a branch on my fork aiming for 1.1 or 2.0. I'm not sure how complex it will become and how many changes will need to be made, but if it's something that everyone else is happy with, I'll give it a go. Should be a more robust approach than the query strings and dispatcher we use now.
Title: Re: Router and Dispatcher
Post by: Joshua Dickerson on September 09, 2015, 04:59:33 pm
Agreed (http://www.elkarte.net/community/index.php?topic=1030). (also (http://www.elkarte.net/community/index.php?topic=1803.msg11354#msg11354))
Title: Re: Router and Dispatcher
Post by: Ant59 on September 10, 2015, 11:35:03 am
Okay, so here's my plan based on the research I've done on other router implementations.

Routing
  • Admin installs/uninstalls addon, or upgrades Elkarte
  • Routes are discovered and added to a new Router
  • The Router then compiles the routes to a single regex and dumps to a cache file

Dispatching
  • User makes a URI request
  • Dispatcher is created and performs a preg_match on the cached routes regex
  • If it gets a match, it calls the method from the controller as defined and passes any remaining variables named in the route

I read an interesting article comparing different ways of producing the regex: https://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html The article suggests that the fastest route matching can be achieved by using dummy groups to count the routes, reducing overhead of indexing in the regex, and by chunking the regex patterns to 10 at a time to prevent quadratic build-up of dummy groups.

I'm going to attempt to implement his suggested "GCB-10C" method.
Title: Re: Router and Dispatcher
Post by: Joshua Dickerson on September 10, 2015, 07:53:54 pm
Thanks for the link to that. Interesting read. Thinking about if/how I can implement it in the BBC, smiley, autolink, and HTML parsers.
Title: Re: Router and Dispatcher
Post by: Ant59 on September 15, 2015, 06:31:11 am
I feel like I'm re-inventing the wheel here and to be honest, I really can't seem to get anything better than what is already offered by libraries on GitHub. Should I just go ahead and grab a routing library and just adapt it for the Elkarte dispatcher?
Title: Re: Router and Dispatcher
Post by: Joshua Dickerson on September 15, 2015, 09:17:08 am
I'd just use Silex.
Title: Re: Router and Dispatcher
Post by: Ant59 on September 15, 2015, 01:29:34 pm
Okay, I have a working prototype which handles basic routes and can do preliminary reverse routing for template links.

https://github.com/Ant59/Elkarte/tree/dispatcher
https://github.com/elkarte/Elkarte/compare/development...Ant59:dispatcher
Title: Re: Router and Dispatcher
Post by: emanuele on September 20, 2015, 11:28:53 am
Sorry for the late reply, I didn't mark it as unread by mistake and I lost it... :-[

Quote from: Ant59 – Should I just go ahead and grab a routing library and just adapt it for the Elkarte dispatcher?
I wouldn't be against. ;D
Title: Re: Router and Dispatcher
Post by: Spuds on September 20, 2015, 06:40:21 pm
That phroute router looks really cool ... nice job implementing it so far :D