-
Notifications
You must be signed in to change notification settings - Fork 3
Routable classes
Note: This page assumes that you have a copy of the relevant code close at hand.
All requests in Eregansu are processed by one or more instances of subclasses of Routable.
Routable itself is very simple. Besides the constructor, and a method — addCrumb — which is used to automatically generate a breadcrumb, the main point of use is the processRequest method.
processRequest is overridden by descendants in order to change how requests are processed. It accepts a single parameter, an instance of the Request class (or rather, one of its subclasses), and performs an action based upon that.
The implementation of processRequest in Routable doesn’t do anything at all besides setting $this->crumbName and calling the addCrumb method. If you wanted to, you could subclass Routable, override processRequest, and put the entire logic of your application in there. It wouldn’t be very easy to maintain, but it would work.
For example, if you create app/test1.php containing:
class Test1 extends Routable
{
public function processRequest(IRequest $request)
{
die("This is Test1::processRequest()\n");
}
}
…and add the following to your appconfig.php:
define('APP_CLASS', 'Test1');
define('APP_CLASS_PATH', 'test1.php');
…then when you invoke your application (either through a web browser, or via the eregansu command-line script), it will produce the output This is Test1::processRequest() and terminate.
The Router class extends Routable to attempt to match parameters in a Request against a defined list of routes. The route maps are stored in the $sapi property, which is an associative array where the key is the name of a SAPI (http is used for the various web server SAPIs), and the value is an array of routes which make up an individual route map. Each route map is itself an associative array, where the key is the name of the route and the value is a further associative array of parameters.
The Router class “consumes” one parameter from the path portion of the request URL (by calling Request::consume) and checks to see if an entry exists in the route map whose key matches the parameter. If no parameter could be consumed, Router checks instead for an entry whose key is __NONE__. In either case, if no match could be found, Router checks for an entry named __DEFAULT__. If no route could be matched at all, Router::processRequest calls Router::unmatched, which by default generates an error (Error::ROUTE_NOT_MATCHED).
For convenience, Router sets $this->routes to be a reference to $this->sapi['http'].
A simple example. Create a file named app/router.php, containing the following class:
class MyRouter extends Router
{
public function __construct()
{
parent::__construct();
$this->routes['test'] = array('file' => 'test1.php', 'class' => 'Test1');
}
}
Next, modify your appconfig.php to include the following (replacing the equivalent lines added earlier):
define('APP_CLASS', 'MyRouter');
define('APP_CLASS_PATH', 'router.php');
If you navigate to your application, you should receive a “Route not matched” error. Feel free to modify MyRouter to override the unmatched method inherited from the parent to see what happens.
If you navigate to /test in your application, you’ll see that the output produced is that from Test1::processRequest.
Route maps can specify any class, provided it’s a class of IRequestProcessor, which in practice means any subclass of Routable. Because Router satisfies this criteria, you can build chains of routers, each consuming an element of the path in the request, and passing it along the chain until the end is reached.
The while the Router class is principally concerned with forwarding requests to other classes, the Proxy class is concerned with generating output itself. Specifically, the Proxy class is designed to act as a front-end to objects which are stored somewhere, allowing actions to be performed upon them, and representing them in different ways.
Proxy is a subclass of Router; the functionality of Proxy is implemented by overriding the unmatched method. This means that an instance of Proxy (or a subclass of it) can behave both as a router and as a destination. The unmatched implementation in Proxy performs several steps:
- Determines the method (that is,
GET,POST,PUT,DELETE, and so on) from the request - Invokes
getObject, which is responsible for retrieving objects referenced by a request (getObjectmay, depending upon what’s being requested, relay the request on to an instance of another class) - Checks that the request method is supported this instance (by matching against the
$supportedMethodsproperty) - Checks that this instance supports generating a response in a form acceptable to the client (by matching the
$supportedTypesproperty against theAcceptheader from the request) - Performs the requested method by invoking the
perform_METHODmethod (e.g.,perform_PUT) - If the method isn’t listed in the
$noFallThroughMethodsarray, invoke theGETmethod handler (i.e., invokesperform_GET).