URI Dispatcher
The CSF dispatch library is a delegating URI dispatcher, meaning that rather than forcing the URI-to-action mapping on the application in a top-down manner, the dispatch process is split into stages which can be customised or replaced. This means that there are no barriers to implementing completely custom behaviour while still taking advantage of the dispatcher.
The dispatch library is deliberately context-unaware. The role of the dispatch library is to route the request URI to the correct “action”, and return whatever response that action produced. The response can be any arbitrary object, only the action and the caller need know how to deal with it.
The following diagram shows how data might typically flow through an application using the CSF dispatch library.
Routes
A “route” maps from an URI pattern to a controller name and an URI “rewrite”. Routes are processed in the order they're defined, so earlier routes take precedence over later ones. The URI pattern is a regular expression (as understood by preg_match), anchored to match against the beginning of the URI. The rewrite is a preg_replace replacement—normal “matched string” placeholders apply, with $0 being the part of the string matched by the whole pattern. Routing is the first stage of dispatch, passing a request to the correct controller.
To illustrate how this fits together, here's an example:
$routes = array( 'blog/' => array( 'controller' => 'Blog', 'rewrite' => '', ), '.*' => array( 'controller' => 'Pages', 'rewrite' => 'show/$0', ), );
The first route here will be applied to anything that starts with blog/. Because the rewrite is empty, the matched part of the URI is removed. Therefore if a request is made for blog/view/12 an instance of the “Blog” controller is created and the rewritten URI, view/12, is dispatched to it.
The second route can match anything, but will not match anything starting with blog/ because the first route will instead. If a request is made for foo/bar an instance of the “Pages” controller will be created and show/foo/bar will be dispatched to it.
This example is actually typical of a personal site that contains a blog and a page hierarchy. It should be evident that the use of regular expressions allows for a very configurable route map.
Controllers
The controller is the second stage of dispatch, taking a rewritten URI and returning a response. The only requirement for a controller class is that it has a dispatch_uri function that does this. A controller should implement some group of functionality, for example actions related to a particular part of a web application.
A common pattern in web frameworks is to use URIs in the format controller/action/arg1/arg2, resulting in a call to controller::action('arg1', 'arg2'). This pattern can also be used with the CSF dispatch library: a route definition can map from any URI prefix to any controller class, and the CSF_Controller class implements the mapping to an action.
While the dispatch library is capable of this pattern, it can also be used in many other ways. For example, a controller class could be created which only performs one action (implemented directly in the dispatch_uri function)—file downloads might be an example of this.
CSF_Dispatch
CSF_Dispatch is the class which performs the routing (first) stage of URI dispatch. It follows the CSF options convention for configuration, and has a single public method (dispatch_uri) for dispatching a URI and returning the response.
Options
| Name | Description | Default |
|---|---|---|
| controller_path | Directory containing controllers | empty string |
| routes | Dispatcher route map | empty array |
| class_prefix | Prefix to controller name to get controller class name | empty string |
| class_suffix | Suffix to controller name to get controller class name | empty string |
| case_sensitive | Perform case-sensitive route matching? | true |
dispatch_uri
The dispatch_uri function actually performs the routing stage of the URI dispatch.
- Match the URI (passed as the first argument) against defined routes until one matches successfully
- Form the class name by concatenating the
class_prefix, controller name andclass_suffix - If the class doesn't exist, attempt to load it from
controller_path/classname.php - Create an instance of the controller class
- Rewrite the URI according to the route and pass it to the
dispatch_urimethod of the controller instance - Return the return value of dispatching to the controller
CSF_Controller
CSF_Controller is a generic controller base class. It is most useful if URIs contain an action/arg1/arg2/… pattern. The URI is split using / as the separator, and the first part is used as the action while the rest are used as arguments to that action. If there is no action part (i.e. if the URI is empty) then it defaults to “index”. Actions must be existing public methods of the controller.
CSF_Controller inherits from CSF_Module, giving derived controllers easy access to CSF modules using $this->modulename.
To take advantage of this URI handling pattern, application controllers should derive from CSF_Controller. For example:
class BlogController extends CSF_Controller { public function show($id) { $res = new TemplatedResponse('blog_show'); $res->post = $this->db->query("SELECT * FROM blog WHERE id = ? LIMIT 1", $id)->fetch(); return $res; } }
dispatch_uri
dispatch_uri dismantles the URI and calls the appropriate action, returning the return value of that action.
- URI is split, using the
/character as a separator - If the array is empty, the action is “index”, otherwise the first element of the array is the action
- Check that the action method exists and is public
- Call the action method with the rest of the URI parts array as arguments
- Return the return value of the action
For example, calling dispatch_uri('addtogroup/Cool People/19') would return the value of addtogroup('Cool People', '19').
